SOFTMOA TECHNOLOGY

경비 영수증 중복 청구 검수 시스템 구축 방법

소프트모아가 정리한 기술 기록입니다.

Contents
작성일 2026. 10. 18.

같은 영수증이 직원 두 명의 경비 청구에 들어왔을 때, 파일을 지우는 것만으로는 재청구를 막기 어렵습니다. 거래일·가맹점·금액이 같은 건을 검토 후보로 모으고, 이미 지급한 이력까지 대조해야 합니다. 아래는 실제 고객 사례가 아닌 설계 예시입니다. 소프트모아의 맞춤 업무 시스템을 검토할 때 필요한 입력과 승인 기준을 설명합니다.

파일이 같은 것과 지출이 같은 것은 다릅니다

사진을 다시 찍으면 같은 영수증도 파일이 달라집니다. 반대로 같은 파일을 첨부했어도 단순 보완 제출일 수 있습니다. 검토 화면은 파일 이름보다 청구 ID, 거래일, 가맹점 식별자, 통화, 금액, 직원, 지급 상태를 나란히 보여주는 편이 낫습니다. 가맹점 이름만 입력받았다면 지점이나 표기 차이를 먼저 정리하고, 식별하지 못한 가맹점은 SQL의 NULL로 통일해 보완을 요청합니다.

이 예시는 회사 한 곳의 KRW 지출만 다루며 금액 단위는 원 단위 정수입니다. 거래일은 YYYY-MM-DD 형식의 유효한 날짜, 금액은 양수로 입력 검증을 마친다고 가정합니다. 환불·분할 결제·다른 통화는 별도 규칙이 필요합니다. 같은 날 같은 식당에서 두 사람이 각각 12,000원을 썼다면 정상 지출 두 건일 수도 있으므로, 값의 일치는 부정 청구의 증거가 아닙니다.

직원 제출부터 지급 담당자 확인까지

  • 직원은 영수증과 거래 정보를 입력합니다. 보완 업로드는 기존 청구에 연결하고, 신규 청구와 구분합니다.
  • 제출 화면은 같은 회사의 제출·승인·지급 건을 대조합니다. 직원이 달라도 비교하되, 신청자에게 다른 직원의 영수증이나 개인정보를 노출하지 않고 검토 필요 여부만 표시합니다.
  • 경비 검토자는 권한이 있는 비교 화면에서 원본 증빙과 거래 내역을 확인합니다. 승인번호가 있으면 가맹점 등과 함께 대조하고, 정상 별도 지출인지 중복 입력인지 사유를 남깁니다.
  • 중복 입력은 미지급 청구를 반려하거나 철회 처리하되 원본과 판정 이력을 남깁니다. 이미 지급한 건은 삭제하지 않고 별도 정정 절차로 넘깁니다.
  • 지급 담당자는 미해결 후보가 없고 승인된 청구만 지급 대상으로 확정합니다. 승인 상태와 실제 송금 완료 상태는 따로 기록합니다.

7건을 넣어 후보가 왜 나오는지 확인하기

다음은 테이블을 만들거나 수정하지 않는 SQLite 조회입니다. WITH로 예제 데이터를 만들고, 윈도 함수로 같은 가맹점·거래일·금액의 행 수를 계산합니다. 윈도 함수는 행을 한 줄로 합치지 않으므로 각 청구 ID를 유지하면서 비교할 수 있습니다.[1][2] 실행 전 sqlite3 --version으로 버전을 확인하세요. 윈도 함수가 필요하며, 아래 결과는 Python sqlite3 모듈의 SQLite 3.53.1에서 읽기 전용 메모리 DB로 확인했습니다. macOS sqlite3 CLI 3.54.0에서도 같은 결과를 확인했습니다.

WITH claim(id, staff, merchant, day, won, status) AS (
  VALUES
  ('C1','E1','M1','2026-10-06',12000,'submitted'),
  ('C2','E2','M1','2026-10-06',12000,'approved'),
  ('C3','E3','M2','2026-10-06',12000,'submitted'),
  ('C4','E4','M1','2026-10-07',12000,'submitted'),
  ('C5','E5','M1','2026-10-06',12000,'withdrawn'),
  ('C6','E6',NULL,'2026-10-06',12000,'submitted'),
  ('C7','E7','M1','2026-10-06',12000,'paid')
), checked AS (
  SELECT *, COUNT(*) OVER (
    PARTITION BY merchant, day, won
  ) AS matches
  FROM claim
  WHERE status IN ('submitted','approved','paid')
)
SELECT id,
  CASE WHEN merchant IS NULL THEN '정보 보완'
       WHEN matches > 1 THEN '후보 검토'
       ELSE '일치 없음' END AS result
FROM checked
ORDER BY id;

조회 결과는 다음과 같습니다.

C1 | 후보 검토
C2 | 후보 검토
C3 | 일치 없음
C4 | 일치 없음
C6 | 정보 보완
C7 | 후보 검토

C1·C2·C7은 직원이 달라도 조건이 같아 후보입니다. 지급된 C7을 비교 대상에 남긴 이유는 새 청구가 과거 지급 건과 겹치는지 보기 위해서입니다. C3은 가맹점, C4는 날짜가 달라 일치하지 않습니다. C5는 미승인·미지급 상태에서 철회한 입력으로 가정해 제외했습니다. C6은 가맹점이 없어 비교를 끝내지 못했으므로 ‘일치 없음’ 대신 ‘정보 보완’을 반환합니다.

C2만 철회해 다시 조회해도 C1과 C7은 여전히 후보로 남습니다. 한 행을 처리했다고 묶음 전체가 해결되는 것은 아닙니다. 비교 대상이 아예 없으면 결과도 0행입니다. 화면에서는 이 경우를 조회 실패와 구별하고, 보완 건이 남아 있는데도 ‘중복 없음’으로 요약하지 않아야 합니다.

검토 결과를 지급 허용으로 바로 바꾸지 않기

‘일치 없음’은 이 세 항목으로 같은 행을 찾지 못했다는 뜻입니다. 날짜 오입력이나 가맹점 매핑 오류가 있으면 실제 중복도 놓칩니다. 정상 별도 지출로 판정한 후보는 비교한 청구 ID와 입력 버전, 사유, 검토자를 저장하고, 금액·증빙이 바뀌면 다시 검토하도록 설계합니다. 후보 목록이 사라졌다는 사실만으로 승인하지는 않습니다.

또한 이 조회는 동시에 들어오는 승인·지급을 잠그지 않습니다. 지급 확정 시 최신 상태를 트랜잭션 안에서 다시 확인하고, 같은 청구의 지급 지시가 재전송되어도 한 번만 처리되도록 별도 장치를 마련해야 합니다. 세무상 증빙 적격성이나 보관 의무를 판정하는 코드도 아닙니다. 원본 증빙 접근 권한과 회사의 회계 처리 기준은 별도로 정해야 합니다.

SQL 문법 참고: [1] SQLite Window Functions · [2] SQLite WITH Clause. 업무 흐름과 데이터는 설명용 예시입니다.

소프트모아는 해당 시스템을 구축합니다. 문의하기

Share this

Information

Have a questions?

견적 및 기술문의

mobile : 010-7931-4813

Contact Form