pharmacy-pos-qr-system/docs/결제수납구조.md
thug0bin a3ff69b67f feat: 알림톡 발송 로그 시스템 + 현영 표시 + 문서화
- 알림톡 발송 로그: alimtalk_logs SQLite 테이블 + DB 자동 기록
- /admin/alimtalk 페이지: 서버 로그, NHN Cloud 내역 조회, 수동 발송 테스트
- 적립일시 포맷 수정: %Y-%m-%d %H:%M (16자 초과) → %m/%d %H:%M (11자)
- POS GUI 현금영수증(현영) 표시: 청록색 볼드
- 결제수납구조.md: CD_SUNAB/PS_main/SALE_MAIN 3테이블 관계 문서
- 실행구조.md: Flask 서버 + Qt GUI 실행 가이드

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 19:28:29 +09:00

9.3 KiB

PIT3000 판매/조제/수납 데이터 구조

핵심 테이블 관계

CD_SUNAB (수납/결제) ─── 모든 거래의 결제 기록 (130건/일 기준)
  │
  ├── PS_main (처방접수) ─── 조제 건만 (89건/일 기준)
  │     │  조인: PS_main.PreSerial = CD_SUNAB.PRESERIAL
  │     │  조인: PS_main.Indate = CD_SUNAB.INDATE
  │     │
  │     ├── PS_sub_hosp (처방 의약품 상세)
  │     └── PS_sub_pharm (조제 의약품 상세)
  │
  └── SALE_MAIN (OTC 판매) ─── OTC 직접 판매만 (39건/일 기준)
        │  조인: SALE_MAIN.SL_NO_order = CD_SUNAB.PRESERIAL
        │
        └── SALE_SUB (판매 품목 상세) ─── SL_NO_order로 조인

테이블별 역할

1. CD_SUNAB — 수납/결제 (모든 거래 포함)

  • 역할: 조제 + OTC 모든 거래의 결제/수납 기록
  • 1주문 = 1행 (복수행 없음)
  • : PRESERIAL (주문번호), INDATE (수납일)
  • 건수: 하루 약 130건 (조제 91 + OTC 39)
컬럼 설명
PRESERIAL 주문번호 (PS_main.PreSerial 또는 SALE_MAIN.SL_NO_order와 매칭)
INDATE 수납일 (YYYYMMDD)
DAY_SERIAL 일련번호
CUSCODE 고객코드
ETC_CARD 조제 카드결제 금액
ETC_CASH 조제 현금결제 금액
ETC_PAPER 조제 외상 금액
OTC_CARD 일반약 카드결제 금액
OTC_CASH 일반약 현금결제 금액
OTC_PAPER 일반약 외상 금액
pAPPROVAL_NUM 카드 승인번호
pMCHDATA 카드사 이름
pCARDINMODE 카드 입력방식 (1=IC칩)
pTRDTYPE 거래유형 (D1=일반승인)
nCASHINMODE 현금영수증 모드 (1=발행, 2=카드거래 자동세팅)
nAPPROVAL_NUM 현금영수증 승인번호
Appr_Gubun 승인구분 (1, 2, 9 등)
APPR_DATE 승인일시 (YYYYMMDDHHmmss)
DaeRiSunab 대리수납 여부
YOHUDATE 요후일
54개 컬럼

2. PS_main — 처방전 접수 (조제 전용)

  • 역할: 처방전 기반 조제 접수 기록
  • : PreSerial (처방번호 = CD_SUNAB.PRESERIAL)
  • 건수: 하루 약 89건
  • SALE_MAIN에는 없음 — 조제건은 SALE_MAIN을 거치지 않음
컬럼 설명
PreSerial 처방번호 (= CD_SUNAB.PRESERIAL)
Day_Serial 일일 접수 순번 (1~89)
Indate 접수일 (YYYYMMDD)
CusCode 환자 코드
Paname 환자명
PaNum 주민번호
InsName 보험구분 (건강보험, 의료급여 등)
OrderName 의료기관명
Drname 처방의사명
PresTime 접수 시간
PRICE_T 총금액
PRICE_P 본인부담금
PRICE_C 보험자부담금
Pre_State 처방 상태
InsertTime 입력 시간
58개 컬럼

3. SALE_MAIN — OTC 직접 판매

  • 역할: 일반의약품(OTC) 직접 판매 기록
  • : SL_NO_order (주문번호 = CD_SUNAB.PRESERIAL)
  • 건수: 하루 약 39건
  • 조제건은 포함되지 않음
컬럼 설명
SL_NO_order 주문번호 (= CD_SUNAB.PRESERIAL)
SL_DT_appl 판매일 (YYYYMMDD)
SL_NM_custom 고객명 (대부분 빈값 → [비고객])
SL_MY_total 원가 (할인 전)
SL_MY_discount 할인 금액
SL_MY_sale 실판매가 (= total - discount)
InsertTime 입력 시간
PRESERIAL 처방번호 (OTC는 'V' 고정, 의미 없음)
30개 컬럼

데이터 흐름 정리

조제 (처방전 기반)

처방전 접수 → PS_main 생성 → 조제 → CD_SUNAB 수납 기록
                                    (ETC_CARD/ETC_CASH에 금액)
  • SALE_MAIN에는 기록되지 않음
  • SALE_SUB에도 품목이 들어가지 않음
  • 환자명은 PS_main.Paname에 있음

OTC 판매 (직접 판매)

POS에서 품목 선택 → SALE_MAIN + SALE_SUB 생성 → CD_SUNAB 수납 기록
                                                (OTC_CARD/OTC_CASH에 금액)
  • PS_main에는 기록되지 않음
  • 고객명은 보통 빈값 ([비고객])

조제 + OTC 동시 (하루 약 10건)

처방전 조제 + 일반약 동시 구매
→ PS_main (조제 부분)
→ SALE_MAIN + SALE_SUB (OTC 부분)
→ CD_SUNAB 1행에 ETC + OTC 금액 모두 기록

조인 키 관계

CD_SUNAB.PRESERIAL = PS_main.PreSerial     (조제건)
CD_SUNAB.PRESERIAL = SALE_MAIN.SL_NO_order (OTC건)

주의: SALE_MAIN.PRESERIAL은 OTC에서 항상 'V'로, 조인키가 아님. 실제 조인키는 SALE_MAIN.SL_NO_order임.


건수 관계 (2025-02-25 기준)

구분 건수 설명
CD_SUNAB 130 모든 수납 기록
PS_main 89 처방전 접수 (= 조제)
SALE_MAIN 39 OTC 직접 판매
CD_SUNAB에만 존재 91 조제건 (SALE_MAIN 없음)
PS_main 매칭 89 91건 중 PS_main과 매칭
미매칭 2 PS_main 없이 수납만 존재 (미수금 수납 등 특수 케이스)

130건 = 39 (OTC) + 89 (조제) + 2 (특수)


조제/OTC 구분 방법

CD_SUNAB의 ETC/OTC 금액으로 판별:

etc_total = ETC_CARD + ETC_CASH  # 조제 금액
otc_total = OTC_CARD + OTC_CASH  # 일반약 금액

if etc_total > 0 and otc_total > 0:
    구분 = "조제+판매"
elif etc_total > 0:
    구분 = "조제"
elif otc_total > 0:
    구분 = "판매(OTC)"
else:
    구분 = "본인부담금 없음"  # 건강보험 전액 부담

결제수단 판별

card_total = ETC_CARD + OTC_CARD
cash_total = ETC_CASH + OTC_CASH

# 현금영수증 판별 (nCASHINMODE=2는 카드거래 자동세팅이므로 제외)
has_cash_receipt = (nCASHINMODE == '1' and nAPPROVAL_NUM != '')

if card_total > 0 and cash_total > 0:
    결제 = "카드+현금"
elif card_total > 0:
    결제 = "카드"
elif cash_total > 0:
    결제 = "현영" if has_cash_receipt else "현금"
else:
    결제 = "-"

GUI 표시 색상

결제 컬럼

  • 카드: 파란색 (#1976D2)
  • 현영: 청록색 볼드 (#00897B) — 현금영수증 발행
  • 현금: 주황색 (#E65100) — 현금영수증 미발행
  • 카드+현금: 보라색 (#7B1FA2)
  • -: 회색 (수납 없음)

수납 컬럼

  • : 녹색 (#4CAF50)
  • -: 회색 (미수납)

할인 표시

  • 할인 없음: 12,000원
  • 할인 있음: 54,000원 (-6,000) 주황색 볼드 + 툴팁

SALE_MAIN 금액 컬럼 상세

컬럼 설명 예시
SL_MY_total 원가 (할인 전) 60,000
SL_MY_discount 할인 금액 6,000
SL_MY_sale 실판매가 (= total - discount) 54,000
SL_MY_recive 수납금액 (부가세 제외 추정) 49,091
SL_MY_credit 외상 금액 0
SL_MY_dis_ratio 할인율 0 (미사용)

금액 관계

SL_MY_sale = SL_MY_total - SL_MY_discount
SL_MY_recive ≈ SL_MY_sale / 1.1  (부가세 제외 금액 추정)

할인 빈도

  • 대부분의 거래: discount = 0 (할인 없음)
  • 할인 적용 건: 하루 2~5건 정도 (직원 할인, 대량 구매 등)
  • 할인 규모: 1,000원 ~ 수십만원까지 다양

CD_SUNAB 카드/현금 상세 컬럼

카드 상세 정보

컬럼 설명 예시
pMCHDATA 카드사 이름 비씨카드사, NH농협카드
PCardName 카드사 이름 (별도) KB국민카드
pAPPROVAL_NUM 카드 승인번호 72139919
pCARDINMODE 카드 입력 방식 1 (IC칩)
pTRDTYPE 거래 유형 D1 (일반승인)
Appr_Gubun 승인 구분 9 (정상승인)
pCANCEL_NUM 취소 승인번호 (취소 시)

현금 상세 정보

컬럼 설명 예시
nCASHINMODE 현금영수증 입력 방식 1=실제발행, 2=카드거래 자동세팅
nAPPROVAL_NUM 현금영수증 승인번호 116624870
nCHK_GUBUN 현금 체크 구분 KOV, TASA

SQL 쿼리 (현재 GUI에서 사용)

SELECT
    M.SL_NO_order,
    M.InsertTime,
    M.SL_MY_sale,
    ISNULL(M.SL_NM_custom, '[비고객]') AS customer_name,
    ISNULL(S.card_total, 0) AS card_total,
    ISNULL(S.cash_total, 0) AS cash_total,
    ISNULL(M.SL_MY_total, 0) AS total_amount,
    ISNULL(M.SL_MY_discount, 0) AS discount,
    S.cash_receipt_mode,
    S.cash_receipt_num
FROM SALE_MAIN M
OUTER APPLY (
    SELECT TOP 1
        ISNULL(ETC_CARD, 0) + ISNULL(OTC_CARD, 0) AS card_total,
        ISNULL(ETC_CASH, 0) + ISNULL(OTC_CASH, 0) AS cash_total,
        nCASHINMODE AS cash_receipt_mode,
        nAPPROVAL_NUM AS cash_receipt_num
    FROM CD_SUNAB
    WHERE PRESERIAL = M.SL_NO_order
) S
WHERE M.SL_DT_appl = ?
ORDER BY M.InsertTime DESC

한계: SALE_MAIN 기준이므로 OTC 판매(39건)만 표시됨. 조제건(~89건)은 표시되지 않음. 조제건까지 보려면 CD_SUNAB을 기본 테이블로 사용하거나 PS_main과 조인하는 쿼리 재설계 필요.


카드사 분포 (전체 데이터 기준)

카드사 건수
KB국민카드 6,106
NH농협카드 5,172
비씨카드사 4,900
하나카드 4,880
신한카드 3,210
삼성카드사 2,100
현대카드사 1,960
우리카드 1,285
롯데카드사 837
카카오페이 57
모바일상품권 11