구매 · 수입

SAP 구매 B/L·입고·송장 3자 대조 — 지급 전에 선적·입고·송장 수량과 단가를 한 줄에서 견주는 수입 대금 점검

세 문서를 한 줄에 · 판정 코드 · 송장 금액의 수량 효과와 단가 효과 분해 · 월별 · 통화별 · 판정별 집계 · 스스로 하는 대사 검증 — 소개 영상과 실제 화면 8종, 그리고 CDS 코드까지

소개 영상블로그 목차 순서대로 · 자막 포함7개 장면 · 1분 23초처음 연 화면 → 점검 필요만 조회 → 판정별 집계 → 라인 상세 → 대사 결과

도입 포인트 — 이 앱을 사용해야 하는 이유

수입 대금을 내보내기 전의 질문은 늘 같습니다. 선적된 만큼 들어왔나, 들어온 만큼 송장이 왔나, 단가는 계약대로인가. 지금 이 세 답은 선적서류 · 창고 입고 내역 · 회계 송장으로 흩어져 있어, 한 건이 어긋나도 서로 다른 화면을 열어 맞춰 보기 전에는 알 수 없습니다. 이 앱은 세 문서를 같은 줄에 놓고, 어긋난 줄에는 이유까지 붙여 지급 전에 먼저 볼 건을 가립니다.

한 줄 요약 — 조회만 하는 리포트는 어긋남을 보여 줄 뿐 이유는 사람이 찾습니다. 이 앱은 송장 금액을 입고 기준 금액 + 수량 효과 + 단가 효과로 풀어 차이의 원인을 수량과 단가로 갈라 주고, 화면의 모든 숫자가 서로 맞는지 대사식 일곱 가지로 스스로 검산합니다. 최종 판단은 사람이 하며, 이 화면은 점검 후보를 가리는 도구입니다.

핵심 포인트 여섯 가지

포인트고객이 얻는 것지금 방식이라면
① 세 수량을 한 줄에선적 · 입고 · 송장 수량과 계약 · 송장 단가를 라인마다 나란히 놓습니다. 어긋난 칸은 숫자만 봐도 드러납니다.선적서류 · 입고 내역 · 송장을 각각 열어 엑셀에서 맞춥니다.
② 어긋남에 판정이 붙는다입고 수량 차이 · 송장 수량 차이 · 단가 차이 · 입고 전 송장 · 송장 미도착을 코드로 가르고, 허용 범위 안의 차이는 안내로 따로 둡니다.어느 줄을 먼저 봐야 하는지 사람이 눈으로 찾습니다.
③ 송장 금액을 효과로 풀기차이 금액을 수량 효과와 단가 효과로 쪼갭니다. 합이 송장 금액과 어긋나지 않는지 150라인 전부를 검산했습니다.차이 금액만 보고 '왜' 는 공급자 · 창고에 메일로 묻습니다.
④ 월 · 통화 · 판정으로 접어 보기같은 150라인을 월별 · 통화별 · 판정별로 묶어 보고 원화 환산 금액을 함께 둡니다.집계표를 매달 새로 만들고, 합계가 다르면 원인을 찾는 회의를 엽니다.
⑤ 스스로 하는 검산선적 = 입고 + 미입고, B/L 합계 = 라인 합계, 월별 = 통화별 같은 일곱 대사를 전수로 돌려 차이 건수를 화면에 적습니다.합계가 맞는지는 만든 사람의 말에 기댑니다.
⑥ 허용 오차를 분리수량 2% · 단가 1% · 송장 대기 30일을 가정값으로 두고 판정과 분리했습니다. 회사 기준으로 바꾸면 같은 화면이 그 기준으로 판정합니다.기준이 파일마다 다르고, 바뀐 기준을 아는 사람이 따로 있습니다.

이 자료에서 드러난 것

검증용 자료는 2026년 7월~9월 선적분 B/L 60건 · 라인 150행입니다. 판정 결과는 정상 93 · 안내 22 · 점검 필요 35이고, 점검 필요는 입고 수량 차이 12 · 송장 수량 차이 7 · 단가 차이 5 · 입고 전 송장 5 · 송장 미도착 6 으로 갈립니다. 같은 점검 필요라도 원인이 다르면 연락할 곳이 다릅니다 — 입고 수량 차이는 창고와 공급자, 송장 수량 · 단가 차이는 송장을 보낸 쪽, 입고 전 송장은 회계입니다. 판정이 원인까지 붙어 있어야 확인 요청이 한 번에 갈 수 있습니다.

가장 단순한 사례는 BL-2609-003 의 첫 라인입니다. 선적 800 · 입고 640 · 송장 800, 단가는 3.71 로 같습니다. 송장 2,968.00 USD 가 입고 기준 2,374.40 에 수량 효과 593.60 이 얹힌 값이고 단가 효과는 0 입니다. 즉 단가가 아니라 수량 때문이며, 원화로는 +832,702원(환율 1,402.80, 가정값)입니다.

하지 않는 일 — 지급을 막거나 풀지 않고, 회계 처리나 세무 신고 판단을 대신하지 않습니다. 최종 판단은 회사와 감사인이, 세무 신고는 회사와 세무 대리인 · 관세사가 합니다. 허용 오차와 환율은 가정값이므로 도입 때 회사 기준으로 정해야 합니다.

실행 화면

실제로 돌아가는 화면 8종을 순서대로 싣습니다. 그림을 누르면 크게 볼 수 있고, 화면마다 무엇을 하는 자리이고 왜 그렇게 두었는지를 아래에 적었습니다. 숫자는 모두 같은 자료에서 나온 것이라 화면끼리 서로 맞춰 보셔도 됩니다.

처음 열었을 때

처음 연 화면 — 세 수량이 한 줄에
처음 연 화면 — 세 수량이 한 줄에 — 선적 연월을 정하고 조회하면 같은 B/L 라인의 선적 · 입고 · 송장 수량과 계약 · 송장 단가가 나란히 놓입니다. 위쪽 요약에는 대조 라인 50 · B/L 20 · 송장 금액 2,540,006,757원 · 점검 필요 12가 뜹니다.

조회조건은 선적 연월(필수) · 통화 · 공급자 일부 · 점검 상태 · 판정 · B/L 일자 범위입니다. 조회를 누르거나 입력 칸에서 엔터를 누르면 같은 조회가 돌고, 조건 영역 맨 위 안내 문장이 이 화면은 점검 후보를 가리는 도구라는 것과 허용 오차가 가정값이라는 것을 먼저 밝힙니다. 표에서는 BL-2609-003 의 첫 라인처럼 선적 800 · 입고 640 · 송장 800 이 한눈에 어긋나 보이고, 점검 필요는 주황 글씨로 칠해 눈에 먼저 들어옵니다.

점검 필요만 조회 — 확인할 12라인
점검 필요만 조회 — 확인할 12라인 — 점검 상태를 '점검 필요' 로 고르면 확인할 라인만 남습니다. 입고 수량 차이 · 송장 수량 차이 · 단가 차이 · 입고 전 송장 · 송장 미도착이 섞여 나옵니다.

정상 38라인을 걷어 내고 12라인만 남긴 모습입니다. 송장 금액은 380,432,976원, 송장−입고 차이는 +18,958,806원으로 요약이 같이 바뀝니다. 같은 B/L 에서도 BL-2609-006 의 첫 라인은 송장 미도착(송장 0), 둘째 라인은 송장 수량 차이(315 대 300)로 서로 다른 판정을 받습니다. 판정이 라인 단위라서 한 B/L 안에서도 원인이 갈립니다.

문서와 집계로 접어 보기

B/L 요약 — 문서 단위로 접어 보기
B/L 요약 — 문서 단위로 접어 보기 — 라인을 B/L 한 건으로 묶어 상태 · 공급자 · 구매오더 · 통화 · 선적 금액 · 입고 기준 금액 · 송장 금액 · 송장−입고 차이 · 환율을 한 줄로 보여 줍니다.

B/L 에 점검 필요 라인이 하나라도 있으면 그 B/L 이 점검 필요가 됩니다. BL-2609-005 는 입고가 모자란 라인 때문에 +36,792.00(JPY) 이 생겼고, BL-2609-009 는 선적 83,790 대비 입고 기준 금액이 18,134.50 에 그쳐 송장이 먼저 온 모양이 드러납니다. 통화가 서로 달라 금액은 통화별로 읽고, 원화 환산은 오른쪽 열과 다른 탭에서 봅니다.

판정별 — 어느 유형이 많은가
판정별 — 어느 유형이 많은가 — 선적 연월마다 판정 유형의 라인 수와 원화 차이를 모아 보여 줍니다. 202609 는 정상 32 · 단가 차이 2 · 송장 수량 차이 2 · 입고 수량 차이 5 · 입고 전 송장 1 · 송장 미도착 3 · 허용 범위 내 5 입니다.

금액이 큰 쪽과 건수가 많은 쪽이 다르다는 것이 여기서 드러납니다. 송장 미도착 3건은 −38,654,106원으로 가장 크지만 건수는 입고 수량 차이(5건)가 더 많습니다. 어느 점검부터 할지 건수와 금액 두 축으로 정하게 됩니다. 판정 코드는 코드표(CodeSet)에서 읽어 이름과 상태를 붙입니다.

월별 대사 — 석 달을 한 표에
월별 대사 — 석 달을 한 표에 — 선적 연월별 B/L 수 · 라인 수 · 송장 금액(원화) · 송장−입고 차이 · 정상 · 안내 · 점검 필요를 한 줄씩 보여 줍니다.

2026년 7월 3,834,507,254원(점검 12) · 8월 4,504,681,126원(점검 11) · 9월 2,540,006,757원(점검 12). 월마다 B/L 20건 · 라인 50행입니다. 송장−입고 차이는 7월 +119,066,668원, 8월 +11,656,833원, 9월 −7,299,374원으로 방향까지 달라, 합계 금액만 보면 놓칠 흐름이 보입니다.

통화별 — 환율 영향을 분리
통화별 — 환율 영향을 분리 — 선적 연월과 통화마다 선적 · 입고 기준 · 송장 금액(통화별)과 차이, 원화 환산 송장과 차이, 점검 필요 건수를 보여 줍니다.

202609 의 JPY 는 송장 11,671,955.00 으로 입고 기준보다 112,392.00 많고, EUR 는 반대로 −8,590.20 입니다. 통화별 차이와 원화 차이의 부호가 같다는 점, 그리고 원화로 환산하면 EUR 쪽이 −13,090,606원으로 가장 크다는 점이 한 표에서 읽힙니다. 환율이 다른 통화끼리는 통화별 금액을 더하지 않습니다.

숫자를 믿어도 되는가

대사 결과 — 스스로 한 검산
대사 결과 — 스스로 한 검산 — 선적 = 입고 + 미입고, 송장 = 입고 기준 + 수량 효과 + 단가 효과, 원화 환산, B/L 합계, 월별 합계, 판정 건수, 상태 건수까지 일곱 줄을 전수로 돌린 결과입니다.

01 번은 선적 수량 합 126,700 이 입고 + 미입고와 같고, 02~05 번은 송장 원화 합 10,879,195,137원이 어느 방향으로 더해도 같다는 것을 보입니다. 검사 건수와 차이 건수가 함께 적히고 차이 건수는 모두 0 입니다. 화면의 숫자를 믿어도 되는지 묻는 자리에서 가장 먼저 열어 보시면 됩니다.

라인 하나를 풀어 보기

라인 상세 — 송장 금액을 효과로 풀기
라인 상세 — 송장 금액을 효과로 풀기 — 라인을 누르면 상세 창이 열립니다. 송장 금액 2,968.00 = 입고 기준 2,374.40 + 수량 효과 593.60 + 단가 효과 0.00 으로 풀리고, 점검 결과 문장이 '선적 대비 입고 수량 -20.00% 차이 — 점검 필요' 로 적힙니다.

아래에는 같은 B/L 의 다른 라인이 표로 붙습니다. BL-2609-003 에서 001 라인만 입고 수량 차이이고 002 · 003 은 3자 일치입니다. 원화로는 +832,702원(환율 1,402.80)입니다. 차이가 수량 때문인지 단가 때문인지를 이 한 창에서 가를 수 있어, 공급자에게 보낼 확인 요청의 내용이 곧바로 정해집니다.

SAP 표준 기능 확장 포인트

이 앱은 표준을 대체하지 않습니다. 선적 · 입고 · 송장은 표준이 이미 기록하고 있고, 끊기는 곳은 세 문서를 한 줄에 놓는 자리입니다. 표준이 하는 일은 그대로 두고 그 자리만 이어 붙입니다.

표준에서 어디를 보는가

문서 · 기능표준에서 보는 곳표준이 못 하는 일이 앱이 더하는 것
선적(B/L)인바운드 납품(LIKP · LIPS), B/L 번호는 납품 헤더의 BOLNR선적 수량을 입고와 송장 옆에 놓고 비교하지 않습니다.선적 수량을 기준으로 입고 수량률을 계산합니다.
입고MIGO · 구매 이력(EKBE 의 입고 유형)입고 후 송장이 얼마나 늦는지 한눈에 보지 못합니다.입고일 기준 송장 대기 일수를 판정에 씁니다.
송장MIRO · 구매 이력(EKBE 의 송장 유형)송장 입력 때만 검증하고, 입력 뒤 어긋남은 다시 모아 주지 않습니다.입고 기준 금액과의 차이를 수량 · 단가 효과로 풉니다.
허용 오차송장 검증 허용 오차 설정(OMR6)선적 기준 수량 차이에는 쓰이지 않습니다.같은 허용 오차 개념을 점검 판정에 씁니다(이 자료에서는 가정값).
차단된 송장MRBR이미 차단된 뒤에 어느 라인이 원인인지는 사람이 찾습니다.차단 이전에 원인 라인을 미리 보여 줍니다.
GR/IR 잔액MB5S잔액은 보여 주지만 선적 기준 문제까지 가르지 않습니다.잔액이 생긴 라인의 판정을 붙여 줍니다.
환율환율표(TCURR)송장 통화 금액의 원화 환산 차이를 라인별로 모아 주지 않습니다.통화별 · 원화 차이를 같은 표에서 봅니다.

표의 표준 이름은 일반적인 배치를 적은 것입니다. 실제 환경에서는 사용자 정의 필드나 별도 입고 · 송장 프로세스가 끼어 있을 수 있어, 도입 전에 어느 필드가 B/L 번호를 들고 있는지부터 확인합니다.

이 앱의 서비스 구성

엔티티셋건수(검증용 자료)한 행이 뜻하는 것
LineSet150B/L 라인 한 줄 — 세 수량 · 단가 · 판정 · 금액 분해
BlSet60B/L 한 건 — 라인을 묶은 문서 단위
MonthSet3선적 연월 하나 — 월별 합계와 상태별 건수
CurSet12연월 × 통화 — 통화별 금액과 원화 환산
CodeSet23연월 × 판정 코드 — 건수와 원화 차이
ReconSet7대사 한 줄 — 좌변 · 우변 · 검사 건수 · 차이 건수

서비스 이름은 blmatch_srv 이고 OData V2 입니다. 화면은 $filter · $orderby · $top · $inlinecount 만 쓰며, 동작 이름이 붙은 주소 · 사용자 정의 파라미터 · 함수 임포트가 없습니다. 이 단순함이 운영 서비스로 바꿀 때 가장 큰 장점입니다 — 같은 주소를 CDS 기반 서비스가 이어받으면 화면은 고치지 않아도 됩니다.

대사 결과 — 전수 검증

번호대사검사 건수차이 건수
01선적 수량 = 입고 수량 + 미입고 수량1500
02송장 금액 = 입고 기준 금액 + 수량 효과 + 단가 효과1500
03송장 원화 = 송장 외화 × 환율(가정)1500
04B/L 합계 = 라인 합계600
05월별 합계 = 통화별 합계30
06판정 코드별 건수 합 = 대조 라인 수30
07정상 + 안내 + 점검 필요 = 대조 라인 수30

모든 대사에서 차이는 0 입니다. 송장 원화 합계 10,879,195,137원이 라인별 · B/L별 · 월별 · 통화별 어느 경로로 더해도 같습니다.

CDS 구성

이 사례의 화면은 서비스가 내려 준 150라인을 브라우저가 묶어 보여 줍니다. 데모라서 되는 일이고, 운영 데이터에서는 그렇게 하지 않습니다. 운영으로 올릴 때 가장 먼저 하는 일이 집계와 판정을 CDS 로 내리는 것입니다. 아래는 그때 만드는 뷰들을 레이어 순서대로 적은 것입니다.

코드는 스케치입니다. 필드 이름과 표준 뷰 이름은 릴리스 · 환경에 따라 다르므로 그대로 붙여 넣기 전에 View Browser(F2170) 로 실제 이름을 확인해야 합니다. 확인이 필요한 자리는 주석에 적어 두었습니다.

뷰 레이어 구성

레이어뷰하는 일왜 나누나
기준ZMM_BLM_TOL허용 오차 · 대기 일수판정 기준을 코드에 박지 않으려는 것입니다.
차원ZI_BlmBlLine납품에서 B/L 라인을 읽음B/L 번호가 놓인 자리를 한 곳에 모읍니다.
집계ZI_BlmHist구매 이력의 입고 · 송장을 품목마다 합침역분개 처리 규칙이 한 곳에만 있게 합니다.
큐브ZI_BlmMatch세 문서 이어 붙임 + 수량 · 단가 효과효과 식을 화면이 아니라 여기서 한 번 정의합니다.
쿼리ZC_BlmMatchQuery판정 코드 부여허용 오차 비교가 이 뷰에 있어야 설명할 수 있습니다.
권한ZI_BLMMATCH(DCL)구매조직 범위집계를 읽는 자리에 걸어야 뺄셈으로 새지 않습니다.
서비스ZUI_BLMATCH_SRV엔티티셋 노출화면과의 계약을 이름으로 고정합니다.

① 허용 오차 테이블

허용 오차는 판정이 갈리는 기준이라 코드에 박지 않고 테이블로 둡니다. 운영에서는 송장 검증 허용 오차(OMR6) 설정을 그대로 옮기거나 이 테이블에서 읽습니다. 유효기간을 둔 이유는 기준이 바뀌어도 과거 판정이 흔들리지 않게 하기 위해서입니다.

@EndUserText.label : '3자 대조 허용 오차'
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #C        " 고객 유지 — 전송(TR)으로 옮긴다
@AbapCatalog.dataMaintenance : #ALLOWED
define table zmm_blm_tol {
  key mandt    : mandt not null;
  key bukrs    : bukrs not null;          " 회사코드
  key valid_to : abap.dats not null;      " 유효 종료일
  qty_pct      : abap.dec(5,2);           " 수량 허용률(%)   이 자료 2
  prc_pct      : abap.dec(5,2);           " 단가 허용률(%)   이 자료 1
  wait_days    : abap.int2;               " 송장 대기 일수   이 자료 30
}

② 선적 라인 뷰

B/L 번호는 납품 헤더에 있고 수량은 납품 품목에 있습니다. 구매오더 번호 · 품목으로 이어 붙이는 자리이므로 B/L 번호를 어느 필드가 들고 있는지가 환경마다 다릅니다.

@AbapCatalog.viewEnhancementCategory : [#NONE]
@AccessControl.authorizationCheck : #NOT_REQUIRED
@EndUserText.label : '선적 라인'
define view entity ZI_BlmBlLine
  as select from likp as h
  inner join      lips as i on i.vbeln = h.vbeln
{
  key h.bolnr                     as BlNo,          " 확인 필요 — 환경에 따라 사용자 정의 필드
  key i.posnr                     as BlItem,
      i.vgbel                     as PurchaseOrder, " 선행 문서가 구매오더일 때
      i.vgpos                     as PurchaseOrderItem,
      h.lfdat                     as BlDate,
      i.meins                     as Unit,
      @Semantics.quantity.unitOfMeasure : 'Unit'
      i.lfimg                     as ShippedQty
}
where i.vgtyp = 'V'                                  " 구매오더 참조 납품

③ 입고 · 송장 집계 뷰

구매 이력(EKBE) 한 테이블에 입고와 송장이 함께 있습니다. 유형으로 나눠 구매오더 품목마다 합치고, 차변 · 대변 표시(SHKZG)로 역분개를 뺍니다. 입고 취소를 입고 수량에서 빼는지는 합의 항목입니다.

@EndUserText.label : '구매 이력 집계(입고·송장)'
define view entity ZI_BlmHist
  as select from ekbe
{
  key ebeln                                  as PurchaseOrder,
  key ebelp                                  as PurchaseOrderItem,
      @Semantics.quantity.unitOfMeasure : 'Unit'
      sum( case when vgabe = '1' and shkzg = 'H' then -menge
                when vgabe = '1'                 then  menge
                else 0 end )                 as GrQty,       " 입고
      sum( case when vgabe = '2' and shkzg = 'H' then -menge
                when vgabe = '2'                 then  menge
                else 0 end )                 as IvQty,       " 송장
      sum( case when vgabe = '2' and shkzg = 'H' then -wrbtr
                when vgabe = '2'                 then  wrbtr
                else 0 end )                 as IvAmtDoc,    " 송장 금액(전표통화)
      max( case when vgabe = '1' then budat end ) as GrDate,
      max( case when vgabe = '2' then budat end ) as IvDate,
      meins                                  as Unit
}
group by ebeln, ebelp, meins

④ 3자 대조 뷰 — 효과 분해

세 뷰를 이어 붙이고 수량 효과 · 단가 효과를 이 한 곳에서 정의합니다. 화면에서 계산하지 않는 이유는 식이 한 곳에만 있어야 합계가 어긋날 자리가 없기 때문입니다. 식은 g·p + (q−g)·p + q·(u−p) = q·u 입니다.

@EndUserText.label : 'B/L·입고·송장 3자 대조'
define view entity ZI_BlmMatch
  as select from ZI_BlmBlLine as b
  left outer join ZI_BlmHist  as h
       on  h.PurchaseOrder     = b.PurchaseOrder
       and h.PurchaseOrderItem = b.PurchaseOrderItem
  association [0..1] to ekpo as _Po
       on  _Po.ebeln = b.PurchaseOrder and _Po.ebelp = b.PurchaseOrderItem
{
  key b.BlNo, key b.BlItem,
      b.PurchaseOrder, b.PurchaseOrderItem,
      b.ShippedQty,
      coalesce( h.GrQty, 0 )                          as GrQty,
      coalesce( h.IvQty, 0 )                          as IvQty,
      _Po.netpr                                       as ContractPrice,
      case when h.IvQty > 0 then h.IvAmtDoc / h.IvQty end as InvoicePrice,

      " 입고 기준 금액 = 입고 수량 × 계약 단가
      coalesce( h.GrQty, 0 ) * _Po.netpr              as GrBaseAmt,
      " 수량 효과 = (송장 수량 − 입고 수량) × 계약 단가
      ( coalesce( h.IvQty, 0 ) - coalesce( h.GrQty, 0 ) ) * _Po.netpr  as QtyEffect,
      " 단가 효과 = 송장 수량 × (송장 단가 − 계약 단가)
      coalesce( h.IvQty, 0 ) * ( ( h.IvAmtDoc / nullif( h.IvQty, 0 ) ) - _Po.netpr ) as PriceEffect,

      h.GrDate, h.IvDate, b.BlDate
}

⑤ 판정 쿼리

판정 우선순위와 허용 오차 비교는 쿼리 뷰에 둡니다. 아래 순서는 스케치이며 어느 판정이 먼저 걸리는지는 회사 기준으로 합의해야 합니다. 허용 오차 테이블과 이어 붙이는 자리가 이 뷰입니다.

@EndUserText.label : '3자 대조 판정'
@Analytics.query : true
define view entity ZC_BlmMatchQuery
  as select from ZI_BlmMatch as m
  inner join      zmm_blm_tol as t on t.valid_to >= $session.system_date
{
  key m.BlNo, key m.BlItem,
      m.ShippedQty, m.GrQty, m.IvQty, m.QtyEffect, m.PriceEffect,
      case
        when m.IvQty > 0  and m.GrQty = 0                     then 'NGR'  " 입고 전 송장
        when m.IvQty = 0  and m.GrQty > 0
         and dats_days_between( m.GrDate, $session.system_date ) > t.wait_days
                                                              then 'NIV'  " 송장 미도착·대기
        when abs( m.GrQty - m.ShippedQty ) > m.ShippedQty * t.qty_pct / 100
                                                              then 'QTY'  " 입고 수량 차이
        when abs( m.IvQty - m.GrQty )      > m.GrQty      * t.qty_pct / 100
                                                              then 'IQT'  " 송장 수량 차이
        when abs( m.PriceEffect )          > m.GrBaseAmt  * t.prc_pct / 100
                                                              then 'PRC'  " 단가 차이
        when m.GrQty <> m.ShippedQty or m.IvQty <> m.GrQty    then 'TOL'  " 허용 범위 내
        else 'OK'
      end as Verdict
}

⑥ 권한(DCL)

집계를 읽는 뷰에 구매조직 범위를 겁니다. 합계 뷰에 걸지 않으면 합계에서 빠진 만큼으로 범위 밖 금액을 짐작할 수 있습니다.

@EndUserText.label : '3자 대조 구매조직 권한'
@MappingRole : true
define role ZI_BLMMATCH {
  grant select on ZI_BlmMatch
    where ( PurchasingOrganization ) = aspect pfcg_auth( M_BEST_EKO, EKORG, ACTVT = '03' );
}   " 확인 필요 — 쿼리 뷰에 구매조직 필드를 노출해야 걸립니다

⑦ 서비스 정의

화면이 부르는 엔티티셋을 서비스로 열어 둡니다. 화면은 $filter 등 표준 질의만 쓰므로 이 서비스가 같은 이름의 엔티티셋을 내려 주면 화면은 고치지 않아도 됩니다.

@EndUserText.label : 'B/L·입고·송장 3자 대조 서비스'
define service ZUI_BLMATCH_SRV {
  expose ZC_BlmMatchQuery as LineSet;
  " BlSet · MonthSet · CurSet · CodeSet 은 같은 방식으로 집계 뷰를 노출한다.
  " ReconSet 은 대사식을 SQL 로 돌려 좌변 · 우변 · 차이를 돌려주는 뷰다.
}

화면이 부르는 질의 예

서비스를 바꿔도 화면이 그대로 돌려면 주소와 응답 모양이 같아야 합니다. 아래는 9월 선적분 중 점검 필요만 금액 큰 순으로 열 건 가져오는 질의입니다.

GET /blmatch_srv/LineSet?$filter=Month eq '202609' and Status eq 'CHECK'
    &$orderby=DiffKrw desc&$top=10&$inlinecount=allpages
→ { "d": { "__count": "12", "results": [ … ] } }

운영 전환에서 정해야 할 것

항목정할 것정하지 않으면
허용 오차수량 · 단가 허용률, 송장 대기 일수판정 건수가 사람마다 다르게 읽힙니다.
환율 기준송장일 환율인지 입고일 환율인지원화 차이가 달라지고 회계와 어긋납니다.
B/L 대응납품 한 건과 B/L 한 건의 대응, 분할 선적 처리라인 수량이 갈려 입고 수량 차이가 가짜로 뜹니다.
입고 취소역분개를 입고 수량에서 빼는지입고 수량이 부풀어 차이를 놓칩니다.
권한구매조직 · 플랜트 범위범위 밖 금액이 합계로 새어 나옵니다.
점검 이후누가 무엇을 하는지(차단 · 확인 요청 · 정정)후보만 쌓이고 처리되지 않습니다.

자주 묻는 질문

도입 상담과 데모에서 나올 만한 질문을 네 묶음으로 적었습니다.

숫자와 판정

3자 대조는 표준의 송장 검증과 무엇이 다릅니까?

표준 송장 검증(MIRO)은 송장을 넣는 순간 구매오더와 입고에 견주어 허용 오차를 넘으면 차단합니다. 이 앱은 그 앞뒤를 봅니다. 앞에는 선적(B/L)이 있고 뒤에는 이미 넣은 송장이 있습니다. 선적은 왔는데 입고가 모자란 건, 송장이 입고보다 먼저 온 건, 송장이 아직 안 온 건처럼 차단 이전 · 이후에 흩어진 문제를 한 줄에 모아 보여 주는 점검 화면입니다. 차단 자체는 표준(MRBR)이 계속 맡습니다.

송장 금액 = 입고 기준 금액 + 수량 효과 + 단가 효과, 이 식은 왜 맞습니까?

입고 수량을 g, 송장 수량을 q, 계약 단가를 p, 송장 단가를 u 라 하면 송장 금액 q·u 는 g·p + (q−g)·p + q·(u−p) 와 같습니다. 앞 항이 입고 기준 금액, 가운데가 수량 효과, 끝이 단가 효과입니다. 전개하면 g·p 와 −g·p 가 지워져 교차항이 남지 않으므로 잔차가 구조적으로 0 입니다. 예를 들어 입고 640 · 송장 800 · 단가 3.71 이면 2,374.40 + 593.60 + 0.00 = 2,968.00 입니다. 대사 02 가 150행 전부에서 이 등식을 확인했고 차이는 0 이었습니다.

판정 코드는 어떻게 나뉩니까?

입고 수량 차이(QTY) · 송장 수량 차이(IQT) · 단가 차이(PRC) · 입고 전 송장(NGR) · 송장 미도착·대기(NIV) · 허용 범위 내 차이(TOL) · 3자 일치(OK) 로 나눕니다. 앞의 다섯은 점검 필요, 허용 범위 안의 차이는 안내, 일치는 정상입니다. 이 자료 150라인에서는 점검 필요 35, 안내 22, 정상 93 이었습니다. 어느 판정이 먼저 걸리는지의 우선순위는 도입 때 회사 기준으로 합의해야 하는 항목입니다.

허용 오차 2%, 1%, 30일은 어디서 온 숫자입니까?

가정값입니다. 수량 2% · 단가 1% · 송장 대기 30일을 임의로 두었고 화면 맨 위에도 그렇게 적혀 있습니다. 운영에서는 송장 검증 허용 오차(OMR6) 설정을 그대로 옮기거나, 별도 테이블로 두고 회사 기준으로 바꿉니다. 같은 자료라도 값을 바꾸면 점검 필요 건수가 달라집니다.

환율은 무엇을 씁니까?

통화별 가정 환율(예: USD 1,402.80 · JPY 9.4123 · CNY 194.07 · EUR 1,523.90)을 한 표로 두고 송장 외화 금액에 곱한 뒤 원 단위로 반올림합니다. 운영에서는 환율표(TCURR)에서 송장일이나 입고일 기준 값을 읽습니다. 어느 날짜의 환율을 쓸지는 도입 때 가장 먼저 정해야 하는 항목 중 하나입니다.

송장 미도착과 입고 전 송장은 어떻게 다릅니까?

송장 미도착·대기는 입고는 끝났는데 송장이 아직 없는 건입니다. 기준일에서 입고일을 뺀 날수가 대기 일수(30일)를 넘으면 점검 필요로 올라갑니다. 입고 전 송장은 반대로 송장은 있는데 입고가 0 인 건입니다. 대금이 나가기 전에 가장 먼저 멈춰야 하는 쪽이라, 금액이 작아도 눈에 띄게 둡니다.

수량 차이가 허용 범위 안이면 정상입니까?

정상이 아니라 안내입니다. 차이가 0 이 아닌 것은 사실이므로 숨기지 않고, 다만 점검 필요로 올리지는 않습니다. 이 자료에서 허용 범위 내 차이는 16건입니다. 안내를 걸러 보고 싶으면 점검 상태를 '점검 필요' 로 고르면 됩니다.

대사 결과 탭은 무엇을 보여 줍니까?

화면의 숫자가 서로 맞는지 스스로 검산한 결과입니다. 일곱 줄이며 선적 = 입고 + 미입고, 송장 = 입고 기준 + 수량 효과 + 단가 효과, 원화 환산, B/L 합계 = 라인 합계, 월별 = 통화별, 판정 건수 합, 정상 + 안내 + 점검 = 라인 수입니다. 좌변과 우변, 검사 건수, 차이 건수가 적히고 이 자료에서는 차이 건수가 모두 0 입니다.

화면과 조회

선적 연월은 왜 필수입니까?

선적 연월 하나로 범위를 먼저 좁히기 때문입니다. 라인은 월마다 수십 건이어도 운영에서는 수만 건이 되므로 한 달 단위로 읽는 편이 안전합니다. 나머지 조건(통화 · 공급자 일부 · 점검 상태 · 판정 · B/L 일자 범위)은 선택이고, 비워 두면 조건을 걸지 않습니다.

조회 버튼 말고 엔터로도 조회됩니까?

됩니다. 입력 칸에서 엔터를 누르면 같은 조회가 돌고, 조회 버튼은 조건 영역 오른쪽 끝에 놓았습니다. '전체' 를 고르면 그 조건은 아예 걸지 않습니다.

탭이 여섯 개인 이유가 있습니까?

같은 150라인을 여섯 번 다르게 묶은 것입니다. 3자 대조(라인), B/L 요약(문서), 월별 대사, 통화별, 판정별, 대사 결과. 합계는 모두 같은 자료에서 나오므로 탭끼리 서로 맞춰 보셔도 됩니다. 이 자료에서 총 송장 금액은 10,879,195,137원이고 월별 · 통화별 · 라인별 어느 쪽으로 더해도 같습니다.

라인을 누르면 무엇이 열립니까?

상세 창에 공급자 · 구매오더 · 품목 · 세 날짜 · 세 수량 · 입고 수량 차이율 · 계약 단가와 송장 단가 · 환율 · 선적 금액과 입고 기준 금액 · 송장 금액의 분해식 · 원화 차이 · 점검 결과 문장이 나오고, 아래에 같은 B/L 의 다른 라인이 표로 붙습니다. 한 B/L 에서 어느 라인이 원인인지 바로 갈립니다.

CSV 로 내려받으면 무엇이 담깁니까?

지금 보고 있는 탭의 표가 그대로 담깁니다. 탭마다 열이 다르므로 3자 대조 탭에서는 라인 단위, 판정별 탭에서는 판정 단위로 나옵니다. 합계를 다시 계산하지 않고 화면의 값을 쓰기 때문에 화면과 파일이 어긋나지 않습니다.

금액이 음수로 나오는 것은 오류입니까?

오류가 아닙니다. 송장−입고 차이는 송장이 입고 기준보다 크면 +, 작으면 −입니다. 송장이 아직 안 온 건은 송장 금액이 0 이라 입고 기준 금액만큼 음수가 됩니다. 이 자료 9월분에서 송장 미도착 3건의 차이가 −38,654,106원이고, 이것이 월 합계를 음수 쪽(−7,299,374원)으로 끌어당깁니다.

서비스와 데이터

화면은 서비스를 어떻게 부릅니까?

OData V2 서비스(blmatch_srv)에 엔티티셋 여섯 개(LineSet · BlSet · MonthSet · CurSet · CodeSet · ReconSet)가 있고, 화면은 $filter · $orderby · $top · $inlinecount 만 씁니다. 동작 이름이 붙은 주소나 사용자 정의 파라미터는 없고 함수 임포트도 없습니다. 조회조건은 모두 필터로 내려갑니다.

날짜 조건은 어떻게 걸립니까?

B/L 일자의 시작과 끝을 각각 걸 때 UI5 가 같은 필드의 두 조건을 or 로 묶어 내보내는 일이 있습니다. 그러면 모든 날짜가 통과합니다. 그래서 시작과 끝을 and 한 묶음으로 만들어 보내고, 서비스는 날짜 필터를 직접 해석합니다. 23건의 날짜 필터 시험이 모두 일치했습니다.

샘플 자료는 실제 거래입니까?

아닙니다. 공급자 이름에 '(가상)' 을 붙인 검증용 가상 자료입니다. B/L 60건 · 라인 150행을 2026년 7월~9월 선적분으로 만들었고 통화는 JPY · CNY · USD · EUR 입니다. 점검 필요 35건은 일부러 심어 둔 사례이며 실제 점검 비율이 아닙니다.

MockServer 는 어디에 씁니까?

시험용 폴더(test)에서만 씁니다. 배포되는 앱은 서비스 폴더의 JSON 을 service.js 가 읽어 같은 주소로 응답하고, 앱은 어느 쪽이든 같은 주소를 부릅니다. 시험 화면과 실제 서비스 화면이 같은 코드로 돌아야 어긋날 자리가 없기 때문입니다.

운영 데이터가 수십만 라인이면 어떻게 됩니까?

선적 연월로 범위를 좁히고 조회는 $top 으로 나눠 받지만, 합계 · 탭 집계를 화면에서 더하는 부분은 느려집니다. 운영에서는 집계를 CDS 뷰로 내려 DB 가 계산하게 하고, 화면은 결과만 받습니다. CDS 구성 절의 뷰 순서가 그 설계입니다.

도입과 운영

이 앱이 최종 판단을 내려 줍니까?

아닙니다. 화면 맨 위에도 적혀 있듯 점검 후보를 가리는 조회 도구입니다. 최종 판단은 회사와 감사인이, 세무 신고는 회사와 세무 대리인 · 관세사가 합니다. 앱은 어느 라인을 먼저 열어 볼지를 정해 줄 뿐 지급을 막거나 풀지 않습니다.

어떤 회사에 맞습니까?

선적 서류와 입고, 송장이 서로 다른 팀의 손을 거치는 수입 회사입니다. 구매는 선적 기준으로 발주를 관리하고, 창고는 입고를 확인하고, 회계는 송장을 넣습니다. 세 곳의 숫자가 한 화면에서 만난 적이 없으면 이 앱이 가장 크게 효과를 냅니다. 반대로 국내 매입만 하고 선적 서류가 없다면 두 문서 대조(2-way)로 충분합니다.

도입 때 가장 먼저 정할 것은 무엇입니까?

허용 오차와 환율 기준, 그리고 B/L 한 건이 납품 몇 건에 대응하는지입니다. 분할 선적 · 분할 입고가 많으면 라인의 수량이 갈리므로 대응 방식부터 정해야 합니다. 도입 시 정해야 할 항목은 CDS 구성 절 끝에 표로 적었습니다.

권한은 어떻게 겁니까?

집계를 읽는 뷰에 구매조직 · 플랜트 범위의 권한(DCL)을 겁니다. 합계 뷰에 걸지 않으면 '내 범위가 아닌 금액이 합계에서 빠진 만큼 보인다' 는 식으로 새는 경로가 생깁니다. 코드는 CDS 구성 절에 있습니다.

AI 는 쓰였습니까?

판정과 금액 계산에는 쓰지 않습니다. 모든 숫자는 규칙으로 계산되고 같은 입력이면 같은 결과가 나옵니다. 판정이 흔들리면 감사 때 설명할 수 없기 때문입니다.

현재 SAP 환경에서 어떻게 적용되는지 궁금합니다.

선적 · 입고 · 송장이 어떻게 기록되는지, 허용 오차를 어떻게 쓰는지, B/L 번호를 어디에 두는지가 회사마다 다릅니다. 왼쪽 목차의 '문의하기' 로 남겨 주시면 환경에 맞춰 어디까지 표준으로 되고 어디부터 확장이 필요한지 함께 살펴 드립니다.