SAP 수입 부대비용 배부 및 수입 원가 확정 점검 — 운임·보험·관세를 품목에 나누고 장부와 맞춰 보는 화면
선하증권(B/L) 단위 원가 확정 · 중량·가액·수량 기준 품목 배부 · 단수 조정 · 환급 가능 수입 부가가치세 제외 · 장부 취득원가 대사 · 선하증권 상세 창 — 소개 영상과 실제 화면 6종, 그리고 CDS 코드까지
소개 영상1분 44초8개 장면음성 안내·자막조회조건 → B/L 원가 확정 → 품목별 배부 → 부대비용 명세 → 상세 → 대사
개발 배경 — 이 앱을 사용해야 하는 이유
수입 담당자의 월말은 같은 질문으로 시작합니다. 이번 달 들어온 B/L 의 수입 원가는 얼마로 확정됐나, 운임·보험·관세·통관 비용은 품목마다 얼마씩 얹혔나, 그 숫자가 장부 취득원가와 맞나. 지금은 이 세 답이 구매오더 화면(ME23N)과 입고 문서(MIGO · MB51), 송장 검증(MIRO), 그리고 포워더와 관세사가 보낸 청구서 엑셀에 흩어져 있습니다. 화면마다 보는 단위가 다릅니다. 구매오더는 품목, 입고는 자재 문서, 송장은 송장 한 장이고, 비용을 청구하는 쪽은 B/L 한 건을 단위로 움직입니다.
이 앱은 그 사이를 이어 붙입니다. 선하증권 한 건을 기준으로 외화 물품 대가를 원화로 옮기고, 그 B/L 에 모인 부대비용을 비용마다 정해진 기준(중량·가액·수량)으로 품목에 나눈 뒤, 수입 원가를 다시 구해 장부 취득원가와 나란히 놓습니다. 새로 계산하는 값은 모두 SAP 표준 데이터 구조(구매오더·자재 문서·송장 검증·회계 전표)에서 읽은 값을 그대로 이어받습니다. 표준 실행은 SAP 표준 T-code 가 담당하고, 이 화면은 조회·검증 관점을 더해 확장합니다.
관련 기준은 IAS 2 재고자산(K-IFRS 제1002호)입니다. 매입원가에 매입가격·수입관세와 취득에 직접 관련된 운송·취급 비용을 포함하고 환급 가능한 세금은 제외한다는 취지의 요구를 화면에서 점검할 수 있도록 풀었습니다. 문단번호와 시행일은 원문 확인 후 정하며, 이 글은 기준서 해석을 대신하지 않습니다.
B/L 한 건의 원가는 송장이 다 모여야 끝난다
배가 도착하고 입고가 되어도 원가는 바로 확정되지 않습니다. 해상운임은 포워더가, 통관수수료는 관세사가, 하역·보관료는 터미널이 따로 청구하고 각자 다른 날 도착합니다. 그동안 구매오더에는 예정 부대비용이 들어 있고, 청구가 도착할 때마다 실제 부대비용이 송장 검증으로 쌓입니다. 마감 시점에는 일부는 확정, 일부는 예정으로 남은 B/L 이 섞여 있습니다.
이 앱은 B/L 한 건마다 예정 합계와 실제 합계, 차이와 차이율을 한 줄에 놓습니다. 같은 입고 연월의 B/L 이 몇 건이고 그중 어디가 예정 대비 허용 범위(샘플은 ±10%, 가정값)를 넘었는지 위쪽 요약에서 먼저 보입니다. 예를 들어 이 글의 샘플 데이터 10월분 5건에서는 실제 부대비용이 52,787,510원이고, 점검이 필요한 B/L 은 4건입니다.
품목에 나누는 기준이 비용마다 다르다
수입 원가는 품목 단위로 쌓입니다. 한 B/L 에 냉연강판 코일, 부품, 포장재가 함께 실려 오면 운임 4,120,000원을 어떻게 나눌지가 곧 품목 원가가 됩니다. 이 앱의 기본 기준은 세 가지입니다. 운임과 하역·보관은 중량으로, 보험료와 관세는 가액으로, 통관수수료는 수량으로 나눕니다. 회사마다 기준이 다를 수 있으므로 기준은 매핑으로 두고 코드에 박지 않았습니다(CDS 절 참고).
나누다 보면 원 단위 이하의 단수가 남습니다. 이 앱은 단수를 마지막 품목에 모아 조정하고, 그 조정액을 컬럼으로 따로 보여 줍니다. 합계는 늘 청구액과 같고, 조정이 어디서 일어났는지도 숨기지 않습니다.
환급되는 수입 부가가치세가 원가에 섞이면 장부가 어긋난다
수입 시점에 내는 부가가치세는 환급 가능한 경우 재고 원가에 넣지 않습니다. 그런데 실무에서는 통관 비용 묶음에 함께 실려 와 취득원가로 들어가 버리는 일이 생깁니다. 이 앱은 부대비용 명세에서 원가 포함 여부를 비용마다 표시하고, 환급 가능 수입 부가가치세는 수입 원가 계산에서 제외합니다. 장부 취득원가가 산출한 수입 원가보다 부가가치세 몫만큼 크게 잡혀 있으면 점검 필요로 띄웁니다. 포함 여부의 최종 판단은 회사와 세무 대리인이 합니다.
예정과 실제의 차이는 마감 때 한꺼번에 드러난다
예정 부대비용이 실제와 크게 다르면 입고 때 잡힌 단가와 마감 때의 원가가 어긋납니다. 하나씩 열어 비교하면 B/L 이 많을수록 시간이 들고, 엑셀로 옮기면 어느 숫자가 최신인지 헷갈립니다. 이 앱은 예정과 실제의 차이를 B/L 행마다 금액과 비율로 나란히 두고, 허용 범위를 넘은 B/L 을 점검 필요로 표시해 먼저 열어 볼 순서를 정해 줍니다.
그래서 무엇을 보려는 앱인가
세 가지입니다. 부대비용이 품목에 빠짐없이 나뉘었는지, 산출한 수입 원가가 장부 취득원가와 맞는지, 그리고 예정과 실제의 차이가 큰 B/L 중 어느 것을 먼저 열어야 하는지입니다. 이 앱은 점검 도구입니다. 원가를 확정하거나 전표를 만들지 않으며, 결과를 근거로 한 판단은 회사와 감사인이, 관세와 부가가치세 신고는 회사와 세무 대리인·관세사가 합니다.
사용 방법
- 조회 조건에서 입고 연월(필수)을 확인합니다. 통화·점검 상태·B/L 일자(부터·까지)는 선택이며, 비워 두면 조건에서 빠집니다.
- 조회 버튼을 누릅니다. 입력 칸에서 Enter 키를 눌러도 같은 조회가 실행되고, 초기화 버튼은 그 옆에 있습니다.
- 위쪽 요약(B/L 건수 · 물품 대가 · 실제 부대비용 · 수입 원가 · 점검 필요 · 장부 대비 차이)을 확인합니다.
- 탭을 차례로 봅니다 — B/L 원가 확정 → 품목별 배부 → 부대비용 명세 → 대사 결과.
- 궁금한 행을 누르면 선하증권 상세 창이 열려 품목과 부대비용을 함께 봅니다.
- 하단 CSV 내려받기로 B/L 원가 확정·품목별 배부·부대비용 명세 탭의 내용을 UTF-8 CSV 로 저장합니다.
숫자를 믿을 수 있는가 — 검증 결과
원가 점검에서 가장 비싼 질문은 “이 숫자 맞아?” 입니다. 그래서 만드는 쪽에서 먼저 대사식을 세워 두고 샘플 데이터 전수로 돌렸습니다.
| 대사식 | 검사 건수 | 차이 건수 | 최대 차이(원) |
|---|---|---|---|
| 배부 전 부대비용 = 품목별 배부액 합계 | 7 | 0 | 0 |
| 예정 + 차이 = 실제 부대비용 | 7 | 0 | 0 |
| 수입 원가 = 물품 대가 + 배부 부대비용 | 7 | 0 | 0 |
| 품목 수입 원가 합계 = B/L 수입 원가 | 7 | 0 | 0 |
| 장부 취득원가와 산출 수입 원가 대사 | 7 | 2 | 1,971,530 |
| USD 외화 금액 × 환율 = 원화 물품 대가 | 2 | 0 | 0 |
| EUR 외화 금액 × 환율 = 원화 물품 대가 | 2 | 0 | 0 |
| JPY 외화 금액 × 환율 = 원화 물품 대가 | 1 | 0 | 0 |
| CNY 외화 금액 × 환율 = 원화 물품 대가 | 2 | 0 | 0 |
장부 대사의 차이 2건은 의도적 예외입니다. 점검 화면을 시연하려고 샘플에 장부 차이 1건, 예정 대비 차이 1건, 부가가치세 포함 1건, 중량 누락 1건(품목 기준)을 일부러 넣었고, 이 가운데 장부와 맞지 않는 두 B/L(장부 차이 1건, 부가가치세 포함 1건)이 위 표의 2건입니다. 나머지 대사식은 모두 차이 0 입니다. 화면 쪽은 별도로 브라우저 자동화를 돌려 탭 이동, 날짜 조건 걸러짐, 상세 창 열림과 닫힘을 다시 확인했습니다.
무엇으로 만들었나
| 자리 | 무엇 | 왜 그렇게 두었나 |
|---|---|---|
| 화면 컨트롤 | OpenUI5 표준 컨트롤(sap.ui.table.Table · sap.m · sap.ui.layout) | 외부 차트·그리드 라이브러리 없이 표준 컨트롤만 써서 사내 보안 심사 부담을 줄입니다. |
| 집계·판정 로직 | 배부 · 단수 조정 · 판정(L01~L04) · 대사 계산을 서비스 로직 한 파일에 모음 | 화면이 계산하지 않고 서비스가 계산한 값만 보여 주어 화면과 숫자가 갈라지지 않습니다. |
| OData 서비스 구성 | OData V2 서비스를 앱 설정(manifest)에 선언하고 이름 없는 기본 모델에 연결. B/L · 품목 · 부대비용 · 대사 네 묶음을 탭마다 바인딩 | 조건은 표준 필터로, 정렬은 표준 정렬로 보냅니다. “전체”는 값을 보내지 않고 조건에서 뺍니다. |
| 오류 처리 | 서비스 정보 실패와 요청 실패를 구분해 메시지로 표시 | 연결 문제와 조건 문제를 사용자가 구분해 조치할 수 있게 합니다. |
| 테마 | sap_horizon | S/4HANA Fiori 와 같은 시각 체계로 보입니다. |
| 항목 | 내용 |
|---|---|
| 솔루션 | 수입 솔루션 |
| 업무 영역 | 구매(MM) · 수입 원가 |
| 관련 기준서·대상 영역 | IAS 2 재고자산(K-IFRS 제1002호) · 수입 원가 확정 |
| SAP 표준 T-code | ME23N · MIGO · MIRO · MB51 · MR11 |
| 화면 성격 | 조회·점검 (배부·집계·대사) |
| 데이터 연동 | OData V2 |
실행 화면
실제로 돌아가는 화면 6종을 사용 순서대로 싣습니다. 그림을 누르면 크게 볼 수 있고, 화면마다 무엇을 보는 자리이고 숫자를 어떻게 읽는지를 아래에 적었습니다. 숫자는 모두 같은 샘플 자료에서 나온 것이라 화면끼리 맞춰 보셔도 됩니다.
처음 열었을 때
입고 연월이 기본으로 들어간 상태에서 조회조건, 요약 지표, 첫 번째 탭이 한 화면에 세로로 쌓입니다. 무엇을 고르면 무엇이 채워지는지를 한눈에 봅니다.

위쪽 요약은 B/L 5건, 물품 대가 375,213,514원, 실제 부대비용 52,787,510원, 수입 원가 428,001,024원, 점검 필요 4건, 장부 대비 차이 +2,156,530원입니다. 수입 원가는 물품 대가와 실제 부대비용의 합이라 두 숫자를 더하면 맞습니다. 표는 B/L 일자가 최근인 것부터 정렬되며, 맨 오른쪽 상태 칸이 점검 필요인 행부터 열어 보면 됩니다.
점검이 필요한 B/L 만 골라 보기
B/L 원가 확정 탭은 선하증권 한 건이 한 줄입니다. 외화 금액과 가정 환율, 원화 물품 대가, 예정과 실제 부대비용, 차이, 수입 원가, 장부와의 차이가 같은 줄에 놓입니다. 점검 상태를 ‘점검 필요’ 로 두고 조회하면 먼저 열어 볼 B/L 만 남습니다.

네 건의 이유가 서로 다릅니다. EUR 건은 실제 부대비용이 예정보다 13.29% 적어 허용 범위(±10%, 가정값)를 벗어났고, JPY 건은 장부 취득원가가 산출 수입 원가보다 1,971,530원 큽니다. USD 건은 장부와 185,000원 차이가 나고, CNY 건은 품목 중량이 비어 배부 기준이 맞지 않습니다. 서로 다른 통화의 외화 금액은 더하지 않으며, 합계는 원화 열에서만 냅니다.
품목에 나누기 — 배부 기준
품목별 배부 탭은 B/L 안의 품목을 한 줄씩 펼칩니다. 운임·보험료·관세·통관수수료·하역·보관이 각각 품목에 얼마씩 얹혔는지가 열로 나뉘고, 오른쪽 끝에서 수입 원가와 단위 원가가 나옵니다.

예를 들어 BL-0001 의 냉연강판 코일(40 EA, 9,600 kg)은 물품 대가 45,441,120원에 배부액 6,324,838원이 붙어 수입 원가 51,765,958원, 단위 원가 약 1,294,149원입니다. 운임은 중량 비율로, 관세는 가액 비율로 나눈 값이라 같은 B/L 의 다른 품목과 비율이 다릅니다. 중량이 비어 있는 품목은 배부가 어긋나므로 점검 필요로 나옵니다.
부대비용 명세 — 무엇이 원가에 들어가나
부대비용 명세 탭은 B/L 에 모인 비용을 한 줄씩 보여 줍니다. 비용마다 배부 기준(중량·가액·수량)과 원가 포함 여부가 붙어 있고, 예정 금액과 실제 금액, 그 차이가 나란히 있습니다.

이 탭은 배부 결과가 왜 그렇게 나왔는지를 거슬러 올라가는 자리입니다. 품목별 배부의 숫자가 이상하면 여기서 해당 비용의 기준과 포함 여부부터 확인합니다. 수입 부가가치세 줄은 금액이 커도 수입 원가 합계에는 들어가지 않습니다.
B/L 에서 품목·비용까지 — 상세 창
표의 행을 누르면 선하증권 상세 창이 열립니다. 탭을 오가지 않고 한 B/L 의 품목 배부와 부대비용 명세를 한 창에서 봅니다.

점검 필요 B/L 을 열었을 때 가장 먼저 보는 화면입니다. 이 예(BL-0005)는 장부 취득원가 194,898,638원이 산출 수입 원가 194,713,638원보다 185,000원 크다는 사유가 위쪽에 적혀 있고, 아래 두 표에서 그 사유가 어느 품목이나 어느 비용에서 왔는지 찾습니다. 닫기 버튼이나 바깥 클릭으로 창을 닫으면 원래 탭의 위치 그대로 돌아옵니다.
대사 — 합계가 맞는지 스스로 점검
대사 결과 탭은 화면이 스스로 하는 검산입니다. 배부 합계와 청구액, 예정과 실제, 산출 원가와 장부를 항목별로 견주고 검사 건수·차이 건수·최대 차이를 적습니다.

대사 결과는 입고 연월과 관계없이 샘플의 B/L 7건 전체를 대상으로 합니다. 정합성 대사는 모두 차이 0 이어야 정상이고, 장부 대사의 차이 2건은 샘플에 일부러 넣은 예외입니다. 통화별 대사는 통화마다 따로 계산하며 서로 다른 통화를 더하지 않습니다. 운영 데이터에서 이 탭에 차이 건수가 보이면 그 줄의 B/L 부터 상세 창으로 확인합니다.
화면 뒤에서 일어나는 일
여기부터는 화면이 어떤 규칙으로 그렇게 움직이는지를 적었습니다. 도입 검토에서 “그래서 이건 어떻게 계산되느냐” 로 자주 되돌아오는 대목입니다.
원가가 만들어지는 여섯 단계
| 순서 | 산출·대사식 | SAP 데이터 지점 |
|---|---|---|
| 1 | 원화 물품 대가 = 외화 금액 × 가정 환율 | 구매오더(EKKO · EKPO) · 입고(MSEG) |
| 2 | 품목별 배부액 = 부대비용 × 품목 기준 값 ÷ 기준 값 합계 (단수는 마지막 품목에서 조정, 조정액 별도 표시) | 송장 검증(RBKP · RSEG) 부대비용 라인 |
| 3 | 수입 원가 = 물품 대가 + 배부 부대비용 (환급 가능 수입 부가가치세 제외) | 자재 문서(MSEG) 취득원가 |
| 4 | 예정 부대비용 + 차이 = 실제 부대비용 | 구매오더 조건(EKPO) 예정 금액 |
| 5 | 품목 수입 원가 합계 = B/L 수입 원가, 배부 전 부대비용 합계 = 품목 배부액 합계 | — |
| 6 | 장부 취득원가 − 산출 수입 원가 = 장부 대비 차이 | 전표(BKPF · BSEG) |
진행 흐름은 구매오더 → 신용장 개설 → 선적·선하증권 → 통관 → 입고 → 대금 결제입니다. 이 화면은 선적 이후 부대비용이 확정되는 구간을 다룹니다.
배부 기준과 단수 조정
| 비용 | 기준 | 나누는 방법 | 원가 포함 |
|---|---|---|---|
| 해상운임 | 중량(W) | 품목 중량 ÷ B/L 중량 합계 | 포함 |
| 보험료 | 가액(V) | 품목 물품 대가 ÷ B/L 물품 대가 합계 | 포함 |
| 관세 | 가액(V) | 품목 물품 대가 ÷ B/L 물품 대가 합계 | 포함 |
| 통관수수료 | 수량(Q) | 품목 수량 ÷ B/L 수량 합계 | 포함 |
| 하역·보관 | 중량(W) | 품목 중량 ÷ B/L 중량 합계 | 포함 |
| 수입 부가가치세 | 가액(V) | 원가 계산에서 제외 (환급 가능분) | 제외 |
각 비용은 먼저 원 단위로 반올림해 품목에 나눕니다. 이렇게 나눈 값의 합이 청구액과 다르면 그 차이(단수)를 마지막 품목에 더하거나 빼서 합을 맞추고, 그 금액을 ‘단수 조정’ 으로 따로 남깁니다. 어느 품목이 흡수했는지가 늘 같은 규칙(마지막 품목)이라 다시 계산해도 같은 결과가 나옵니다.
점검 판정 규칙
| 판정 조건 | 결과 상태 | 사용자 조치 |
|---|---|---|
| L01 — 장부 취득원가와 산출 수입 원가에 차이가 있음 | 점검 필요 | 차이 항목을 표준 입고·송장 문서에서 확인 |
| L02 — 실제 부대비용이 예정 대비 허용 범위(±10%, 가정값)를 넘음 | 점검 필요 | 예정 단가와 실제 청구 내역 확인 |
| L03 — 환급 가능 수입 부가가치세가 장부 취득원가에 포함된 것으로 보임 | 점검 필요 | 취득원가 제외 처리 여부 확인 |
| L04 — 배부 기준(중량) 값이 빈 품목이 있음 | 점검 필요 | 품목 마스터의 중량 확인 |
| 위 조건이 없음 | 정상 | 조치 없음 |
조회조건
| 조건 | 필수 | 기본값 | 조회 조건으로 보내는 방식 |
|---|---|---|---|
| 입고 연월 | 필수 | 202610 | Period eq '202610' |
| 통화 | 선택 | 전체(조건 없음) | Currency eq 'USD' |
| 점검 상태 | 선택 | 전체(조건 없음) | CheckStatus eq 'CHECK' |
| B/L 일자 부터 | 선택 | 비움 | BlDate ge datetime'…' |
| B/L 일자 까지 | 선택 | 비움 | BlDate le datetime'…' |
“전체” 는 코드값을 보내지 않고 해당 조건을 조회 조건에서 뺍니다. 환율과 관세율은 샘플의 가정값이며 실제 고시값이 아닙니다.
결과 컬럼
| 탭 | 컬럼 | 산식·의미 |
|---|---|---|
| B/L 원가 확정 | 외화 금액 · 가정 환율 · 물품 대가 · 예정/실제 부대비용 · 차이 · 차이율 · 수입 원가 · 장부 대비 차이 | 차이율 = (실제 − 예정) ÷ 예정 |
| 품목별 배부 | 수량 · 중량 · 물품 대가 · 운임 · 보험료 · 관세 · 통관수수료 · 하역·보관 · 배부 합계 · 수입 원가 · 단위 원가 | 단위 원가 = 수입 원가 ÷ 수량 |
| 부대비용 명세 | 비용 · 배부 기준 · 원가 포함 · 예정 · 실제 · 차이 | 수입 부가가치세는 원가 제외 |
| 대사 결과 | 구분 · 대사 · 좌변 · 우변 · 검사 건수 · 차이 건수 · 최대 차이 | 통화별 대사는 통화마다 따로 계산 |
좁은 화면에서 달라지는 것
이 화면은 표가 중심이라 1,280px 안팎 이상의 폭을 기준으로 구성했습니다. 폭이 좁아지면 표를 좌우로 밀어 가며 봅니다. 휴대폰 전용 배치는 따로 두지 않았습니다. 회의실 화면이나 노트북에서 쓰는 점검 화면으로 보시는 것이 좋습니다.
파일 구성
| 구분 | 내용 |
|---|---|
| 앱 본체 | index.html · manifest.json · Component.js · controller · view · model · css · i18n |
| 서비스 | 서비스 정의 · 서비스 로직 · 검증용 샘플 데이터 |
| 설명서·영상 | 설명서 한 파일 · 소개 영상과 대표 이미지 |
SAP 표준 기능 확장 포인트
이 앱은 SAP 표준을 대체하지 않습니다. 표준이 이미 담당하는 일은 표준 T-code 에 두고, B/L 한 건을 단위로 묶어 보는 관점만 더하는 쪽으로 만들었습니다. 어디까지가 표준이고 어디서부터 이 화면인지를 먼저 적습니다.
표준으로 되는 것과 안 되는 것
| 하고 싶은 일 | SAP 표준 | 표준 화면의 단위 | 이 앱이 더하는 관점 |
|---|---|---|---|
| 구매오더의 물품 대가·예정 부대비용 확인 | ME23N | 구매오더 문서와 품목 | B/L 한 건에 묶인 구매오더의 예정 합계를 한 줄로 봅니다 |
| 입고 시점 취득원가 확인 | MIGO · MB51 | 자재 문서 한 건 · 문서 목록 | 품목 수입 원가를 배부 결과와 나란히 놓습니다 |
| 부대비용 송장 반영 | MIRO | 송장 한 장 | 송장 합계가 품목 배부 합계와 같은지 대사합니다 |
| 입고·송장 차이 정리 | MR11 | GR/IR 계정 정산 실행 | 정리 전에 차이가 어느 B/L 에서 났는지 먼저 좁힙니다 |
| B/L 한 건 단위의 원가 확정 점검 | — | 문서 단위 화면을 오가며 사용자가 묶어 봅니다 | 예정·실제·배부·수입 원가·장부 차이를 B/L 한 줄에 놓습니다 |
| 중량·가액·수량 기준 배부 비교와 단수 표시 | — | 보통 엑셀에서 계산합니다 | 비용마다 기준을 적용해 품목에 나누고 단수 조정액을 따로 보입니다 |
T-code 별 연계 지점
맞닿은 표준 T-code 마다 이 앱이 이어받는 데이터, 두 화면의 숫자를 맞춰 보는 지점, 오가는 방법, 표준에 남겨 둘 일을 적었습니다.
| T-code | 이름 | 이어받는 데이터 | 숫자를 맞춰 보는 지점 | 오가는 방법 | 표준에 남겨 둘 일 |
|---|---|---|---|---|---|
ME23N | 구매오더 조회 | EKKO · EKPO 의 외화 금액과 예정 부대비용 | 물품 대가(외화) · 예정 부대비용 합계 | 이 앱의 B/L 행 → 구매오더 번호로 ME23N 에서 원천 확인 | 구매오더 변경·승인 |
MIGO | 상품 입고 | MSEG 의 입고 수량과 취득원가 | 품목 수량 · 장부 취득원가 | 품목 행 → 입고 문서를 MIGO 에서 조회 | 입고 전기·취소 |
MIRO | 송장 검증 | RBKP · RSEG 의 부대비용 송장 라인 | 실제 부대비용 = 부대비용 송장 합계 | 부대비용 명세 행 → 송장 번호로 MIRO 에서 확인 | 송장 검증·지급 전기 |
MB51 | 자재 문서 목록 | MSEG 의 금액·수량 이력 | 품목 장부 취득원가 합계 | 이 앱의 장부 대비 차이 → MB51 목록에서 원천 문서 확인 | 문서 이력 조회·감사 대응 |
MR11 | GR/IR 계정 정산 | 입고·송장 차이 문서 | 예정과 실제의 차이가 정산 대상과 맞는지 | 차이 큰 B/L 을 좁힌 뒤 표준 정산 실행 | 정산 실행과 전표 생성 |
운영으로 옮길 때 가장 자주 나오는 질문은 “기존 리포트를 없애야 하나” 입니다. 없애지 않습니다. 표준 화면은 전표를 만들고 문서를 추적하는 실행과 감사 대응의 자리로 그대로 두고, 이 앱은 마감 전에 B/L 단위로 원가를 점검하는 자리로 함께 씁니다. 두 화면의 숫자가 다르면 위 표의 ‘맞춰 보는 지점’ 으로 되돌아가 원천을 확인합니다. 법정 보고와 공시, 관세·부가가치세 신고에 쓰는 수치는 표준과 회사 절차를 따릅니다.
S/4HANA 분석 스택과의 자리
S/4HANA 에는 구매 문서와 재고 원가를 분석하는 표준 CDS 뷰와 Fiori 분석 앱, Analysis for Office 같은 도구가 있습니다. 이 앱은 그 도구들과 경쟁하지 않습니다. 표준 분석이 구매오더·자재 문서·송장을 각각의 관점에서 보여 준다면, 이 앱은 B/L 한 건에 모인 비용을 품목 원가로 풀어 장부와 견주는 점검 관점을 보탭니다. 아래 CDS 절의 뷰는 같은 원천 테이블을 읽는 별도 뷰이므로 표준 뷰를 고치지 않습니다. 표준 CDS 뷰와 Fiori 앱 중 이번 환경에서 확인하지 못한 이름은 이 글에 적지 않았습니다 — 실제 이름은 “확인 필요” 로 남기고 원천 테이블 기준으로 설계했습니다.
확장 포인트 — 운영에서 실제로 손대는 자리
| 자리 | 무엇을 손대나 | 어떻게 |
|---|---|---|
| 부대비용 조건 유형 | 어떤 조건 유형(운임·보험·관세·통관·하역)을 원가 포함 비용으로 볼지 | 매핑 테이블에 유형별 한 줄을 추가합니다 |
| 배부 기준 | 비용 유형마다 중량·가액·수량 중 무엇으로 나눌지 | 같은 매핑 테이블의 기준 컬럼으로 정합니다 |
| B/L 연결 | 선하증권 번호와 구매오더·송장을 어떻게 잇는지 | 고객사의 B/L 관리 방식(확장 필드·커스텀 필드·연결 테이블)에 맞춰 연결 뷰를 둡니다. 확인 필요 |
| 환율 유형·시점 | 물품 대가를 환산하는 환율 유형과 기준일 | 샘플은 가정 환율 고정값. 운영에서는 환율 테이블을 읽는 연결로 바꿉니다 |
| 허용 범위 | 예정 대비 실제 차이를 점검 필요로 볼 기준(샘플 ±10%) | 서비스 로직의 판정 상수 한 곳 |
| 권한 | 구매조직·플랜트별로 보이는 B/L 범위 | CDS 권한 정의에 표준 권한 오브젝트를 겁니다 |
요구사항 매핑표
| 기준서 | 요구사항 | 대응 기능 | 원천 데이터 | 비고 |
|---|---|---|---|---|
| IAS 2 재고자산(K-IFRS 제1002호) | 매입원가에 매입가격·수입관세·취득과 직접 관련된 운송·취급 비용을 포함하고 환급 가능 세금은 제외 | 수입 원가 산출과 환급 가능 부가가치세 제외 표시 | EKPO · MSEG · RSEG | 문단번호는 원문 확인 후 기재 |
| IAS 2 재고자산(K-IFRS 제1002호) | 취득원가를 품목에 합리적 기준으로 배부 | 품목별 중량·가액·수량 배부와 단수 조정 | MSEG | 판단은 회사와 감사인 |
CDS 구성
이 사례의 화면은 서비스 로직이 샘플 데이터로 배부와 대사를 계산합니다. 운영에서는 그 계산의 대부분을 CDS 로 내립니다. 아래는 그때 만드는 뷰와 권한의 구성입니다. B/L 번호는 표준 키가 아니므로, 고객사가 B/L 을 구매오더·송장에 어떻게 잇는지가 먼저 정해져야 합니다(아래 코드의 B/L 연결 테이블은 그 자리를 대신한 스케치입니다).
코드는 스케치입니다. 필드 이름과 표준 객체 이름은 릴리스와 환경에 따라 다르므로, 그대로 붙여 넣기 전에 실제 이름을 확인해야 합니다. 확인이 필요한 자리는 주석에 적어 두었습니다.
뷰 레이어 구성
| 레이어 | 뷰 | 하는 일 | 왜 나누나 |
|---|---|---|---|
| 기준 | ZIMP_COSTBASIS · ZIMP_BLHEAD · ZIMP_BLCOST | 비용 유형 → 배부 기준·원가 포함 매핑, B/L 헤더, B/L 부대비용 | 기준을 코드에 박으면 새 비용 유형이 생길 때마다 개발자를 부르게 됩니다. |
| 차원 | ZI_ImportCostType | 비용 유형 한 행 + 기준 + 포함 여부 + 텍스트 | 큐브에서 join 을 한 번만 걸고 텍스트와 값 도움말이 이 뷰 하나를 봅니다. |
| 집계 | ZI_ImportBlBasis · ZI_ImportBlCostByBasis | B/L 별 기준 값 합계와 기준별 부대비용 합계 | 배부 식의 분모와 분자를 B/L 단위에서 한 번만 정의합니다. |
| 큐브 | ZI_ImportItemAlloc · ZI_ImportLandedItem | 품목별 배부, 단수 조정, 수입 원가 | 화면이 계산하던 배부를 DB 로 내리고 단수 규칙을 한 곳에 둡니다. |
| 쿼리 | ZC_ImportLandedQuery | 조회조건·표시 컬럼·필터 기본값 | 표준 Fiori 와 Analysis for Office 도 같은 쿼리를 띄웁니다. |
| 권한 | ZI_ImportLandedItem (DCL) | 구매조직·플랜트 권한 | 집계를 읽는 자리에 걸어야 합계로 새지 않습니다. |
| 서비스 | ZUI_ImportLandedCost | 서비스 정의와 OData V2 바인딩 | 화면의 manifest 서비스 주소가 가리킬 자리입니다. |
① 비용 유형 → 배부 기준 매핑 테이블
리포트의 모든 숫자가 이 테이블에서 갈립니다. 어떤 비용을 원가에 넣고 무엇으로 나눌지를 정하는 자리라 운영 전환에서 가장 먼저 합의해야 하는 항목입니다. 유효기간을 둔 이유는 조건 유형 체계가 바뀌어도 과거 입고분의 원가가 그때 기준으로 다시 계산되게 하기 위해서입니다.
// ────────────────────────────────────────────────────────────────
// ZIMP_COSTBASIS — 비용 유형 → 배부 기준 · 원가 포함 매핑 (투명 테이블)
// 운영에서는 구매 조건 유형(운임·보험·관세·통관·하역)을 그대로 옮겨 담는다.
// 코드에 박지 않는 이유 : 새 비용 유형이 생길 때마다 개발자를 부르게 되면
// 원가 점검 화면은 금방 낡는다.
// ────────────────────────────────────────────────────────────────
@EndUserText.label : '수입 부대비용 배부 기준'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #C // 고객 유지 — 전송(TR)으로 옮긴다
@AbapCatalog.dataMaintenance : #ALLOWED // 유지보수 뷰를 함께 만들어 둔다
define table zimp_costbasis {
key mandt : mandt not null;
key cost_type : abap.char(4) not null; // FRT 운임 · INS 보험 · DUT 관세 · CLR 통관 · HDL 하역 · VAT 수입 부가세
key valid_from : abap.dats not null;
valid_to : abap.dats;
basis : abap.char(1); // W 중량 · V 가액 · Q 수량
incl_cost : abap.char(1); // X = 원가 포함, 공백 = 제외(환급 가능 세금 등)
sort_no : abap.int4;
}
// ── B/L 연결 : 선하증권은 표준 키가 아니므로 고객사 방식에 맞춰 둔다(확인 필요)
@EndUserText.label : '수입 B/L 헤더'
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
define table zimp_blhead {
key mandt : mandt not null;
key bl_no : abap.char(20) not null;
ebeln : ebeln; // 구매오더
waers : waers; // 거래 통화
fx_amount : abap.curr(15,2); // 외화 금액
bl_date : abap.dats;
period : abap.char(6); // 입고 연월 YYYYMM
}
@EndUserText.label : '수입 B/L 부대비용'
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
define table zimp_blcost {
key mandt : mandt not null;
key bl_no : abap.char(20) not null;
key cost_no : abap.numc(2) not null;
cost_type : abap.char(4);
waers : waers;
plan_amount : abap.curr(15,2); // 예정 — 구매오더 조건에서
act_amount : abap.curr(15,2); // 실제 — 송장 검증 라인에서
}
② 비용 유형 차원 뷰
비용 유형마다 배부 기준과 원가 포함 여부를 한 행으로 가져오는 뷰입니다. 큐브가 매핑 테이블을 직접 읽으면 유효기간 조건이 뷰마다 흩어지므로, 유효기간 판단을 이 뷰 한 곳에 모았습니다.
// ────────────────────────────────────────────────────────────────
// ZI_ImportCostType — 비용 유형 차원
// 배부 기준·원가 포함 여부·표시 순서를 한 행으로 낸다.
// 유효기간 판단은 여기서 한 번만 한다 — 큐브마다 흩어 두면 어긋난다.
// ────────────────────────────────────────────────────────────────
@AccessControl.authorizationCheck : #NOT_REQUIRED
@EndUserText.label : '수입 비용 유형'
@ObjectModel.representativeKey : 'CostType'
@Analytics.dataCategory : #DIMENSION
define view entity ZI_ImportCostType
as select from zimp_costbasis as b
{
@ObjectModel.text.element : ['CostTypeName']
key b.cost_type as CostType,
b.basis as AllocBasis, // W · V · Q
case b.incl_cost when 'X' then 'Y' else 'N' end as InclCost,
b.sort_no as SortNo,
// 이름은 텍스트 테이블을 따로 두거나 도메인 고정값 텍스트를 쓴다 — 확인 필요
cast( b.cost_type as abap.char(40) ) as CostTypeName
}
where b.valid_from <= $session.system_date
and ( b.valid_to is initial or b.valid_to >= $session.system_date )
③ B/L 단위 합계 — 배부 식의 분모와 분자
품목 배부는 비용 ÷ 기준 값 합계 × 품목 기준 값입니다. 이 가운데 분모(기준 값 합계)와 분자의 큰 덩어리(기준별 비용 합계)는 B/L 단위로 한 번만 계산하면 됩니다. 별도 뷰로 두면 큐브는 분수 곱셈만 합니다. 기준 값이 비어 있는 품목은 합계에서 빠지므로 판정(L04)에서 따로 잡습니다.
// ────────────────────────────────────────────────────────────────
// ZI_ImportBlBasis — B/L 별 기준 값 합계 (분모)
// 중량·가액·수량을 B/L 단위로 더하고, 단수를 흡수할 마지막 품목 번호를 함께 낸다.
// ────────────────────────────────────────────────────────────────
@AccessControl.authorizationCheck : #NOT_REQUIRED
@EndUserText.label : '수입 B/L 기준 값 합계'
define view entity ZI_ImportBlBasis
as select from ekpo as i
inner join zimp_blhead as h on h.ebeln = i.ebeln
{
key h.bl_no as BlNo,
sum( cast( i.brgew as abap.dec(23,3) ) ) as TotWeight, // 총중량 — 필드는 확인 필요
sum( cast( i.netwr as abap.dec(23,2) ) ) as TotValue, // 물품 대가 합계
sum( cast( i.menge as abap.dec(23,3) ) ) as TotQty,
max( i.ebelp ) as LastItemNo // 단수를 흡수할 마지막 품목
}
group by h.bl_no
// ────────────────────────────────────────────────────────────────
// ZI_ImportBlCostByBasis — B/L · 배부 기준별 부대비용 합계 (분자)
// 원가에 넣지 않는 비용(환급 가능 수입 부가가치세)은 여기서 걸러 낸다.
// ────────────────────────────────────────────────────────────────
@AccessControl.authorizationCheck : #NOT_REQUIRED
@EndUserText.label : '수입 B/L 기준별 부대비용'
define view entity ZI_ImportBlCostByBasis
as select from zimp_blcost as c
inner join ZI_ImportCostType as t on t.CostType = c.cost_type
{
key c.bl_no as BlNo,
key t.AllocBasis as AllocBasis, // W · V · Q
sum( c.act_amount ) as CostAct
}
where t.InclCost = 'Y'
group by c.bl_no, t.AllocBasis
④ 품목 배부 큐브 — 수입 원가가 만들어지는 곳
이 뷰에서 품목 수입 원가가 결정됩니다. 기준별 비용 합계에 품목 비율을 곱해 원 단위로 반올림하고, B/L 에서 반올림한 값의 합과 청구액의 차이(단수)를 마지막 품목에 더합니다. 단수 규칙을 이 한 뷰에 둔 덕분에 화면·CSV·AI 연계 어디에서 읽어도 같은 숫자가 나옵니다.
// ────────────────────────────────────────────────────────────────
// ZI_ImportItemAlloc — 품목별 기준별 배부 (반올림 값)
// 배부액 = round( 기준별 부대비용 × 품목 기준 값 ÷ B/L 기준 값 합계 , 0 )
// ────────────────────────────────────────────────────────────────
@AccessControl.authorizationCheck : #NOT_REQUIRED
@EndUserText.label : '수입 품목 기준별 배부'
define view entity ZI_ImportItemAlloc
as select from ekpo as i
inner join zimp_blhead as h on h.ebeln = i.ebeln
inner join ZI_ImportBlBasis as b on b.BlNo = h.bl_no
left outer join ZI_ImportBlCostByBasis as cw on cw.BlNo = h.bl_no and cw.AllocBasis = 'W'
left outer join ZI_ImportBlCostByBasis as cv on cv.BlNo = h.bl_no and cv.AllocBasis = 'V'
left outer join ZI_ImportBlCostByBasis as cq on cq.BlNo = h.bl_no and cq.AllocBasis = 'Q'
{
key h.bl_no as BlNo,
key i.ebelp as ItemNo,
b.LastItemNo,
@Semantics.amount.currencyCode : 'Currency'
cast( round( division( coalesce( cw.CostAct, 0 ) * cast( i.brgew as abap.dec(23,3) ),
b.TotWeight, 4 ), 0 ) as abap.curr(15,2) ) as AllocByWeight,
@Semantics.amount.currencyCode : 'Currency'
cast( round( division( coalesce( cv.CostAct, 0 ) * cast( i.netwr as abap.dec(23,2) ),
b.TotValue, 4 ), 0 ) as abap.curr(15,2) ) as AllocByValue,
@Semantics.amount.currencyCode : 'Currency'
cast( round( division( coalesce( cq.CostAct, 0 ) * cast( i.menge as abap.dec(23,3) ),
b.TotQty, 4 ), 0 ) as abap.curr(15,2) ) as AllocByQty,
h.waers as Currency
}
// ────────────────────────────────────────────────────────────────
// ZI_ImportBlRoundSum — B/L 별 반올림 합계와 청구액의 차이(단수)
// ────────────────────────────────────────────────────────────────
@AccessControl.authorizationCheck : #NOT_REQUIRED
@EndUserText.label : '수입 B/L 단수'
define view entity ZI_ImportBlRoundSum
as select from ZI_ImportItemAlloc as a
inner join ZI_ImportBlCostByBasis as c on c.BlNo = a.BlNo
{
key a.BlNo,
sum( a.AllocByWeight + a.AllocByValue + a.AllocByQty ) as AllocRounded,
sum( distinct c.CostAct ) as CostActTotal // 확인 필요 : 기준별 행 중복 주의
}
group by a.BlNo
// ────────────────────────────────────────────────────────────────
// ZI_ImportLandedItem — 품목 수입 원가 (큐브)
// 수입 원가 = 물품 대가(원화) + 배부 합계 + 단수 조정(마지막 품목만)
// ────────────────────────────────────────────────────────────────
@AccessControl.authorizationCheck : #CHECK
@EndUserText.label : '수입 품목 원가'
@Analytics.dataCategory : #CUBE
@ObjectModel.usageType : { serviceQuality : #C, sizeCategory : #XL, dataClass : #TRANSACTIONAL }
define view entity ZI_ImportLandedItem
as select from ZI_ImportItemAlloc as a
inner join ekpo as i on i.ebelp = a.ItemNo
inner join zimp_blhead as h on h.bl_no = a.BlNo and h.ebeln = i.ebeln
inner join ZI_ImportBlRoundSum as r on r.BlNo = a.BlNo
association [0..*] to ZI_ImportCostType as _CostType on 1 = 1
{
key a.BlNo,
key a.ItemNo,
h.period as Period,
i.werks as Plant,
i.ekorg as PurchasingOrg, // 확인 필요 : 헤더 필드일 수 있음
@Semantics.amount.currencyCode : 'Currency'
i.netwr as GoodsFx,
@Semantics.amount.currencyCode : 'Currency'
( a.AllocByWeight + a.AllocByValue + a.AllocByQty ) as AllocBase,
// 단수 : 마지막 품목이 청구액과 반올림 합계의 차이를 흡수한다
@Semantics.amount.currencyCode : 'Currency'
case when a.ItemNo = a.LastItemNo
then r.CostActTotal - r.AllocRounded
else cast( 0 as abap.curr(15,2) ) end as RoundAdj,
a.Currency,
_CostType
}
⑤ 분석 쿼리 — 조회조건과 표시 컬럼
큐브를 그대로 노출하면 사용자가 필터와 컬럼을 처음부터 짜야 합니다. 쿼리 뷰는 입고 연월을 필수 조건으로 두고, 통화·점검 상태를 선택 조건으로, 배부 합계와 수입 원가를 기본 컬럼으로 정합니다. 대용량에서 연월 조건 없이 전체를 읽는 일을 막는 장치이기도 합니다.
// ────────────────────────────────────────────────────────────────
// ZC_ImportLandedQuery — 조회·점검 쿼리
// 입고 연월은 필수 필터. 연월 없이 전 기간을 읽지 못하게 막는다.
// ────────────────────────────────────────────────────────────────
@AccessControl.authorizationCheck : #CHECK
@EndUserText.label : '수입 원가 확정 점검'
@Analytics.query : true
@VDM.viewType : #CONSUMPTION
define view entity ZC_ImportLandedQuery
as select from ZI_ImportLandedItem
{
@AnalyticsDetails.query.axis : #ROWS
@Consumption.filter : { selectionType : #SINGLE, mandatory : true, multipleSelections : false }
Period,
@AnalyticsDetails.query.axis : #ROWS
@AnalyticsDetails.query.displayHierarchy : #OFF
BlNo,
@AnalyticsDetails.query.axis : #ROWS
ItemNo,
@Consumption.filter : { selectionType : #SINGLE, mandatory : false }
Currency,
@AnalyticsDetails.query.axis : #COLUMNS
@Aggregation.default : #SUM
GoodsFx,
@AnalyticsDetails.query.axis : #COLUMNS
@Aggregation.default : #SUM
AllocBase,
@AnalyticsDetails.query.axis : #COLUMNS
@Aggregation.default : #SUM
RoundAdj
}
⑥ 권한 (DCL) — 집계를 읽는 자리에 건다
권한은 쿼리가 아니라 큐브에 겁니다. 쿼리에만 걸면 다른 경로로 큐브를 읽을 때 구멍이 생기고, 드릴스루에만 걸면 합계에서 뺄셈으로 다른 조직의 숫자를 추정할 수 있습니다. 표준 구매 권한 오브젝트(플랜트)를 그대로 재사용하므로 새 역할을 만들지 않고 기존 구매 담당 역할에 조회 권한만 더하면 됩니다.
// ────────────────────────────────────────────────────────────────
// ZI_ImportLandedItem (DCL) — 플랜트 기준 조회 권한
// M_BEST_WRK : 구매 문서 — 플랜트 단위 권한(표준 오브젝트, 필드는 확인 필요)
// ────────────────────────────────────────────────────────────────
@EndUserText.label : '수입 품목 원가 권한'
@MappingRole : true
define role ZI_ImportLandedItem {
grant select on ZI_ImportLandedItem
where ( Plant ) = aspect pfcg_auth( M_BEST_WRK, WERKS, ACTVT = '03' );
}
⑦ 서비스 정의 — 화면이 부르는 서비스
마지막으로 쿼리를 서비스로 노출합니다. 서비스 정의는 무엇을 내보낼지를, 서비스 바인딩은 어떤 프로토콜(OData V2)로 낼지를 정합니다. 바인딩을 게시하고 나면 화면의 앱 설정(manifest)에 적힌 서비스 주소를 이 서비스 주소로 바꾸면 샘플 서비스 대신 운영 데이터가 붙습니다.
// ────────────────────────────────────────────────────────────────
// ZUI_ImportLandedCost — 서비스 정의
// 서비스 바인딩(OData V2 - UI)은 ADT 에서 이 정의를 골라 만들고 게시한다.
// 기존 방식으로 활성화한다면 /IWFND/MAINT_SERVICE 에서 등록한다(확인 필요).
// ────────────────────────────────────────────────────────────────
@EndUserText.label : '수입 원가 확정 점검 서비스'
define service ZUI_ImportLandedCost {
expose ZC_ImportLandedQuery as LandedItem;
expose ZI_ImportCostType as CostType;
}
운영 시점에 해야 할 일
개발보다 정하는 일이 많습니다. 아래는 코드를 쓰기 전에 합의해야 하는 항목입니다.
| 해야 할 일 | 무엇을 정하나 | 정하지 않으면 | 누가 |
|---|---|---|---|
| 부대비용 조건 유형 매핑 | 운임·보험·관세·통관·하역 조건 유형을 어느 비용으로 보고 원가에 넣을지 | 같은 비용이 B/L 마다 다르게 분류되어 원가가 흔들립니다 | 구매팀 · 회계팀 |
| 배부 기준 | 비용 유형마다 중량·가액·수량 중 무엇으로 나눌지 | 사람마다 기준이 달라 품목 원가가 어긋납니다 | 회계팀 · 구매팀 |
| B/L 연결 방식 | 선하증권 번호를 구매오더·송장과 어떻게 이을지(확장 필드·연결 테이블) | 부대비용이 어느 B/L 의 것인지 알 수 없어 배부 자체가 안 됩니다 | 구매팀 · 개발 |
| 환율 유형과 환산 시점 | 물품 대가를 환산하는 환율 유형과 기준일 | 같은 B/L 의 물품 대가가 화면마다 달라집니다 | 자금팀 · 회계팀 |
| 부호·통화 규칙 | 외화 금액과 원화 금액의 표시 규칙, 통화별 소수 자릿수 | 통화별 대사에서 자릿수 차이가 차이로 잡힙니다 | 회계팀 |
| 원천 확정(실적·예정) | 예정은 구매오더 조건, 실제는 송장 검증 라인으로 확정 | 예정과 실제가 섞여 차이율이 의미를 잃습니다 | 구매팀 · 회계팀 |
| 허용 범위 | 예정 대비 실제 차이를 점검 필요로 볼 기준(샘플 ±10%) | 점검 필요가 너무 많거나 너무 적게 뜹니다 | 구매팀 · 회계팀 |
| 권한 설계 | 구매조직·플랜트별로 볼 수 있는 B/L 범위 | 다른 조직의 수입 원가가 드러납니다 | 보안 · 권한 |
| 대사 체계 | 표준 T-code(MB51 · MIRO)와 맞출 항목과 주기 | 숫자가 다를 때 어디부터 볼지 합의가 없습니다 | 회계팀 |
| 전송(TR) 순서 | 매핑 테이블 → 차원·집계 뷰 → 큐브 → 쿼리 → 권한 → 서비스 | 의존 뷰가 없어 활성화 오류가 납니다 | 개발 · Basis |
| 서비스 활성화 | 서비스 바인딩 게시(또는 서비스 등록)와 앱 설정의 서비스 주소 교체 | 화면이 운영 서비스를 찾지 못해 서비스 정보 오류가 납니다 | Basis · 개발 |
운영 데이터로 갈 때 — B/L 과 문서가 수천만 건이면
샘플은 B/L 7건, 품목 18건입니다. 운영에서는 구매오더 품목과 자재 문서가 수천만 건이 될 수 있습니다. 그때 먼저 지킬 원칙은 세 가지입니다. 첫째, 집계는 DB 에서 합니다 — 위 뷰가 그 일을 하므로 화면이 문서를 모두 내려받아 합산하지 않습니다. 둘째, 입고 연월을 필수 조건으로 둡니다 — 쿼리의 필수 필터가 전 기간 조회를 막습니다. 셋째, 조회는 B/L 요약부터 읽고 품목과 비용은 행을 눌렀을 때 읽습니다. 응답 시간 기준(예: 월 단위 요약은 수 초 이내)은 고객사 데이터로 측정해 정해야 하며, 이 글은 측정값을 약속하지 않습니다. 인덱스는 B/L 번호·구매오더 번호·입고 연월에 필요한지 실행 계획을 보고 판단합니다.
자주 묻는 질문
도입 상담과 데모에서 자주 받는 질문들을 네 묶음으로 적었습니다.
숫자와 산식
수입 원가는 어떻게 계산합니까?
선하증권 한 건마다 외화 금액 × 가정 환율로 원화 물품 대가를 구하고, 그 B/L 에 모인 부대비용 가운데 원가에 포함하는 것만 품목에 나눠 더합니다. 즉 수입 원가는 물품 대가 + 배부 부대비용입니다. 환급 가능한 수입 부가가치세는 원가에서 제외합니다.
예를 들어 샘플의 BL-0001 은 물품 대가 112,355,940원, 부대비용 14,323,190원으로 수입 원가가 126,679,130원입니다.
부대비용은 어떤 기준으로 품목에 나눕니까?
비용마다 기준이 정해져 있습니다. 해상운임과 하역·보관은 중량, 보험료와 관세는 가액, 통관수수료는 수량으로 나눕니다. 품목의 기준 값을 B/L 의 기준 값 합계로 나눈 비율을 비용에 곱하는 방식입니다.
이 기준은 샘플의 기본값이며 회사마다 다를 수 있습니다. 운영에서는 비용 유형별 기준을 매핑 테이블에 두므로 코드를 고치지 않고 바꿀 수 있습니다.
나누고 남는 단수는 어떻게 처리합니까?
비용을 품목에 나눌 때 원 단위로 반올림하면 품목 배부액의 합이 청구액과 몇 원씩 어긋날 수 있습니다. 이 앱은 그 차이를 마지막 품목에 더하거나 빼서 합을 청구액과 맞추고, 조정한 금액을 ‘단수 조정’ 으로 따로 보여 줍니다. 어느 품목이 흡수했는지가 늘 같은 규칙이라 다시 계산해도 결과가 같습니다.
환급 가능한 수입 부가가치세는 왜 원가에서 뺍니까?
환급받을 세금은 비용이 아니므로 재고 원가에 넣지 않는다는 것이 관련 기준서의 취지입니다. 이 앱은 부대비용 명세에서 수입 부가가치세 줄을 ‘제외(환급 가능)’ 로 표시하고 수입 원가 계산에서 뺍니다. 장부 취득원가가 이 세금만큼 크게 잡혀 있으면 점검 필요로 띄웁니다.
환급 가능 여부의 판단은 회사와 세무 대리인이 합니다. 이 화면은 그 판단을 대신하지 않고 원가에 섞였는지만 점검합니다.
외화와 환율은 어떻게 다룹니까?
물품 대가는 B/L 의 통화로 들어 있으므로 B/L 마다 외화 금액에 환율을 곱해 원화로 옮깁니다. 서로 다른 통화의 외화 금액은 그대로 더하지 않고, 합계는 원화 환산 금액으로만 냅니다. 대사 결과 탭에서는 통화마다 따로 환산 대사를 합니다.
샘플의 환율은 화면 시연용 가정값이며 실제 고시 환율이 아닙니다. 운영에서는 환율 유형과 환산 시점을 정해 환율 테이블에서 읽도록 바꿉니다.
‘점검 필요’ 는 어떤 조건에서 뜹니까?
네 가지입니다. 장부 취득원가와 산출한 수입 원가가 다를 때(L01), 실제 부대비용이 예정 대비 허용 범위(샘플 ±10%)를 넘을 때(L02), 환급 가능 수입 부가가치세가 장부 취득원가에 포함된 것으로 보일 때(L03), 배부 기준인 중량이 빈 품목이 있을 때(L04)입니다. 어느 조건에도 걸리지 않으면 정상입니다.
상세 창 위쪽에 판정 사유가 문장으로 적혀 있어, 어디부터 확인할지 바로 알 수 있습니다.
화면과 조작
조회조건에서 꼭 넣어야 하는 것은 무엇입니까?
입고 연월 하나입니다. 기본값이 들어 있으므로 바꾸지 않고 조회 버튼만 눌러도 됩니다. 통화, 점검 상태, B/L 일자(부터·까지)는 선택 조건이며 비워 두면 조건에서 빠집니다. ‘전체’ 를 고르는 것도 같은 뜻입니다.
입력 칸에서 Enter 키를 눌러도 같은 조회가 실행되고, 초기화 버튼은 조건을 처음 상태로 돌립니다.
조회한 뒤 어느 탭부터 보면 됩니까?
위쪽 요약에서 점검 필요 건수를 먼저 보고, B/L 원가 확정 탭에서 상태가 점검 필요인 행을 찾는 순서를 권합니다. 그 행을 눌러 상세 창에서 이유를 확인하고, 필요하면 품목별 배부와 부대비용 명세 탭에서 숫자를 거슬러 올라갑니다. 마지막으로 대사 결과 탭에서 정합성 대사에 차이가 없는지 봅니다.
행을 누르면 무엇이 열립니까?
선하증권 상세 창이 열립니다. 거래처, 인코텀즈, 통화, B/L 일자와 입고일, 물품 대가, 실제 부대비용, 배부 합계와 단수 조정, 수입 원가, 장부 취득원가, 환급 가능 수입 부가가치세, 점검 결과가 위쪽에 있고 그 아래에 품목별 배부와 부대비용 명세가 이어집니다. 닫기 버튼이나 창 바깥을 누르면 원래 탭으로 돌아갑니다.
CSV 로 내려받으면 무엇이 담깁니까?
하단 CSV 내려받기 버튼은 B/L 원가 확정, 품목별 배부, 부대비용 명세 탭에서 지금 보고 있는 탭의 내용을 UTF-8 CSV 로 저장합니다. 조회 조건에 맞는 행만 담기고 첫 줄에 컬럼 머리글이 붙습니다. 엑셀에서 한글이 깨지면 열 때 인코딩을 UTF-8 로 지정하시기 바랍니다.
휴대폰이나 좁은 화면에서도 쓸 수 있습니까?
표 중심 화면이라 노트북이나 회의실 화면을 기준으로 구성했습니다. 폭이 좁으면 표를 좌우로 밀어 가며 볼 수 있지만, 휴대폰 전용 배치는 따로 두지 않았습니다. 이동 중 확인이 필요하면 CSV 로 내려받아 쓰는 편이 낫습니다.
표준 T-code 와 데이터
표준 T-code 를 계속 써야 합니까? 기존 리포트를 없애야 합니까?
없애지 않습니다. 구매오더 변경(ME23N), 입고 전기(MIGO), 송장 검증(MIRO), GR/IR 정산(MR11) 같은 실행은 표준 T-code 가 계속 담당합니다. 이 화면은 마감 전에 B/L 단위로 원가를 점검하는 관점을 더해 함께 씁니다.
법정·공시·세무 신고에 쓰는 수치는 표준과 회사 절차를 따르고, 이 화면의 결과는 점검 자료로 씁니다.
이 화면 숫자가 표준 화면과 다르면 어디를 봅니까?
자재 문서 목록(MB51)에서 품목의 취득원가를, 송장 검증(MIRO)에서 부대비용 송장 라인을, 구매오더(ME23N)에서 물품 대가와 예정 부대비용을 확인합니다. 이 앱의 장부 대비 차이는 두 숫자의 차이를 보여 줄 뿐 원인을 단정하지 않으므로, 차이가 난 B/L 의 상세 창에서 품목과 비용을 나눠 어느 쪽이 다른지 좁힌 뒤 해당 T-code 로 원천을 봅니다.
데이터 원천은 무엇이고 정합성은 어떻게 확인합니까?
원천은 구매오더(EKKO · EKPO), 자재 문서(MSEG), 송장 검증(RBKP · RSEG), 회계 전표(BKPF · BSEG)입니다. 정합성은 대사 결과 탭에서 확인합니다. 배부 전 부대비용과 배부액 합계, 예정+차이와 실제, 수입 원가의 구성, 품목 합계와 B/L 합계, 장부와 산출 원가, 통화별 환산을 항목마다 검사 건수·차이 건수·최대 차이로 적습니다.
부대비용 조건 유형이나 계정 체계가 바뀌면 어떻게 합니까?
비용 유형과 배부 기준은 코드가 아니라 매핑 테이블에 있으므로, 새 유형이 생기면 한 줄을 추가하고 기준과 원가 포함 여부를 적으면 됩니다. 유효기간을 두었기 때문에 과거 입고분은 그때의 기준으로 다시 계산됩니다. 계정이 바뀌어 장부 취득원가를 읽는 위치가 달라지면 원천 연결 뷰를 확인해야 합니다.
적용 시기와 범위는 어떻게 됩니까?
이 화면은 점검 도구라 적용 시기가 따로 없습니다. 관련 기준서 IAS 2 재고자산(K-IFRS 제1002호)의 시행일과 경과규정은 원문을 확인한 뒤 적용 범위를 정해야 하며, 이 글은 해당 내용을 다루지 않습니다. 화면의 점검 항목은 회사의 수입 원가 정책에 맞춰 조정해서 씁니다.
도입과 운영
도입하면 무엇이 달라지고 누가 씁니까?
수입 담당자와 회계 담당자가 마감 전에 쓰는 화면입니다. B/L 마다 엑셀로 배부를 다시 계산하고 장부와 맞추던 일이 한 화면의 조회와 상세 확인으로 줄어듭니다. 점검 필요 B/L 을 먼저 골라 볼 수 있어 확인 순서가 정해지고, 같은 기준으로 계산되므로 담당자마다 숫자가 달라지는 일이 줄어듭니다.
운영 시스템에 연결하는 절차와 소요는 어떻게 됩니까?
순서는 정해져 있습니다. 비용 유형·배부 기준·B/L 연결 방식을 합의하고, CDS 뷰와 권한을 만들어 전송한 뒤, 서비스를 게시하고, 화면의 앱 설정에 적힌 서비스 주소를 운영 서비스로 바꿉니다. 소요는 B/L 연결 방식과 합의 속도에 달려 있어 이 글에서 기간을 약속하지 않습니다. 개발보다 정하는 일이 더 많다는 점은 ‘운영 시점에 해야 할 일’ 표에 정리했습니다.
권한과 보안은 어떻게 됩니까?
집계를 읽는 큐브에 표준 구매 권한(플랜트 등)을 겁니다. 쿼리에만 걸면 다른 경로로 읽을 때 구멍이 생기므로 큐브 단계에서 막는 구성입니다. 화면은 OpenUI5 표준 컨트롤만 쓰고 서비스 호출은 OData 로만 이루어집니다. 구체적인 권한 범위는 회사의 구매조직·플랜트 구조에 맞춰 설계해야 합니다.
B/L 과 문서가 아주 많아도 되나요?
집계를 DB 에서 하고, 입고 연월을 필수 조건으로 두고, 품목과 비용은 행을 눌렀을 때 읽는 구성이라 대용량을 염두에 두었습니다. 다만 실제 응답 시간은 고객사의 데이터 규모와 시스템에 달려 있어 이 글은 수치를 약속하지 않습니다. 운영 데이터로 측정한 뒤 인덱스와 조건을 조정하는 단계를 거치시길 권합니다.
점검 결과가 곧 회계·세무 판단입니까?
아닙니다. 이 화면은 분류·집계·대사를 돕는 점검 도구이며 최종 판단은 회사와 감사인이 합니다. 관세와 부가가치세 신고는 회사와 세무 대리인·관세사가 판단합니다. 화면은 ‘차이가 있다’, ‘포함된 것으로 보인다’ 까지만 알려 주고, 그 차이를 어떻게 처리할지는 정하지 않습니다.
샘플의 환율·관세율·거래처는 실제 값입니까?
아닙니다. 샘플 데이터의 환율과 관세율은 시연용 가정값이고 실제 고시값이 아닙니다. 거래처와 품목 이름도 일반 명칭으로 지은 가상의 자료이며 실제 거래와 관련이 없습니다. 화면 위쪽 안내 문구에도 같은 내용이 적혀 있습니다.
정정이나 취소가 있으면 어떻게 반영됩니까?
이 화면은 조회·점검 화면이라 원가를 확정하거나 전표를 만들지 않습니다. 운영 서비스에 연결했을 때 입고 취소나 송장 정정이 표준에서 이루어지면 그 결과가 원천 데이터에 반영되고, 이 화면은 다음 조회 때 바뀐 값으로 다시 계산합니다. 정정 처리 자체는 표준 T-code 에서 하시기 바랍니다.