From aed0c314b71a4d48593b00091875b993f421930e Mon Sep 17 00:00:00 2001 From: thug0bin Date: Fri, 13 Mar 2026 14:57:54 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20DB=20=EA=B5=AC=EC=A1=B0=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20-=20=EC=A0=9C=ED=92=88/=EC=9E=85=EA=B3=A0/=ED=8C=90?= =?UTF-8?q?=EB=A7=A4/=EB=A7=88=EC=A7=84=20=ED=9D=90=EB=A6=84=20=EB=AC=B8?= =?UTF-8?q?=EC=84=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 핵심 테이블 구조 (CD_GOODS, SALE_SUB, WH_sub 등) - 바코드 매핑 구조 (대표바코드, 단위바코드) - 마진 계산 로직 분석 - 마진 0 문제 원인 파악: 입고 없이 판매 시 INPRICE=0 --- docs/DB구조_제품입고판매마진.md | 410 ++++++++++++++++++++++++++++++++ 1 file changed, 410 insertions(+) create mode 100644 docs/DB구조_제품입고판매마진.md diff --git a/docs/DB구조_제품입고판매마진.md b/docs/DB구조_제품입고판매마진.md new file mode 100644 index 0000000..fb93650 --- /dev/null +++ b/docs/DB구조_제품입고판매마진.md @@ -0,0 +1,410 @@ +# 약국 POS DB 구조 - 제품/입고/판매/마진 + +> **분석일**: 2026-03-13 +> **DB**: PM_DRUG, PM_PRES (MSSQL, 192.168.0.4\PM2014) +> **목적**: 입고 기록 없이 판매 시 마진이 0으로 나오는 문제 원인 파악 + +--- + +## 1. 개요 + +약국 POS 시스템(PharmIT3000)은 크게 3개의 DB를 사용합니다: + +| DB명 | 용도 | 주요 테이블 | +|------|------|------------| +| **PM_DRUG** | 약품 마스터, 재고, 입고 | CD_GOODS, IM_total, WH_main/sub | +| **PM_PRES** | 판매, 처방 | SALE_MAIN/SUB, PS_main/sub_pharm | +| **PM_BASE** | 고객, 도매상 정보 | CD_PERSON, CD_custom | + +--- + +## 2. 핵심 테이블 + +### 2.1 제품 마스터 - PM_DRUG.CD_GOODS + +```sql +-- 핵심 컬럼 +DrugCode NVARCHAR(20) -- 제품코드 (PK) +GoodsName NVARCHAR(80) -- 제품명 +BARCODE NVARCHAR(20) -- 대표 바코드 +Price DECIMAL -- 입고가 (매입가) ⭐ 마진 계산의 핵심 +Saleprice DECIMAL -- 판매가 +SUNG_CODE NVARCHAR(9) -- 성분코드 +POS_BOON NVARCHAR(6) -- 분류 (010103=동물약) +GoodsSelCode NVARCHAR(2) -- 사용여부 (B=사용, !=미사용) +``` + +### 2.2 바코드 매핑 + +#### CD_BARCODE - 표준코드 ↔ 바코드 매핑 +```sql +BARCODE NVARCHAR(20) -- 바코드 +BASECODE NVARCHAR(10) -- 표준코드 (EDI 코드) +DRUGCODE NVARCHAR(10) -- 제품코드 +TITLECODE NVARCHAR(20) -- 대표코드 +``` + +#### CD_ITEM_UNIT_MEMBER - 단위별 바코드 (낱개/박스) +```sql +DRUGCODE NVARCHAR(20) -- 제품코드 +CD_CD_UNIT NVARCHAR(3) -- 단위코드 +CD_NM_UNIT REAL -- 단위수량 +CD_CD_BARCODE NVARCHAR(20) -- 단위 바코드 ⭐ APC 코드 (02로 시작) +CD_MY_UNIT DECIMAL -- 단위가격 +``` + +> **APC 코드**: `02`로 시작하는 13자리 바코드 = 동물약 식별 코드 +> 예: CD_ITEM_UNIT_MEMBER에서 `CD_CD_BARCODE LIKE '02%'` + +### 2.3 재고 테이블 + +#### IM_total - 현재 재고 +```sql +DrugCode NVARCHAR(12) -- 제품코드 +IM_QT_sale_debit FLOAT -- 현재 재고 수량 +``` + +#### IM_date_total - 일별 재고 변동 +```sql +IM_DT_appl NVARCHAR(8) -- 날짜 (YYYYMMDD) +DrugCode NVARCHAR(12) -- 제품코드 +IM_QT_sale_credit DECIMAL -- 입고량 +im_qt_sale_debit DECIMAL -- 출고량 +``` + +### 2.4 입고 테이블 (PM_DRUG) + +#### WH_main - 입고 마스터 +```sql +WH_NO_stock NVARCHAR(14) -- 입고번호 (PK) +WH_DT_appl NVARCHAR(8) -- 입고일 (YYYYMMDD) +WH_CD_cust_sale NVARCHAR(10) -- 도매상코드 (→ PM_BASE.CD_custom) +WH_BUSINAME NVARCHAR(200) -- 도매상명 +WH_MY_amount_t DECIMAL -- 입고 총액 +``` + +#### WH_sub - 입고 상세 +```sql +WH_SR_stock NVARCHAR(14) -- 입고번호 (FK → WH_main) +DrugCode NVARCHAR(20) -- 제품코드 +WH_DT_appl NVARCHAR(8) -- 입고일 +WH_NM_item_a DECIMAL -- 입고 수량 ⭐ +WH_MY_unit_a DECIMAL -- 입고 단가 ⭐ +WH_MY_amount_a DECIMAL -- 입고 금액 (수량 × 단가) +WH_END_validity NVARCHAR(8) -- 유효기한 +WH_LOT_NO NVARCHAR(20) -- LOT 번호 +``` + +### 2.5 판매 테이블 (PM_PRES) + +#### SALE_MAIN - 판매 마스터 +```sql +SL_NO_order NVARCHAR(14) -- 거래번호 (PK) +SL_DT_appl NVARCHAR(8) -- 판매일 +SL_CD_custom NVARCHAR(10) -- 고객코드 +InsertTime DATETIME -- 등록시간 +SL_MY_total DECIMAL -- 총액 +SL_MY_sale DECIMAL -- 판매액 ⭐ +SL_MY_sale_cost DECIMAL -- 원가 합계 ⭐ (마진 계산용) +SL_MY_discount DECIMAL -- 할인액 +``` + +#### SALE_SUB - 판매 상세 +```sql +SL_NO_order NVARCHAR(14) -- 거래번호 (FK) +DrugCode NVARCHAR(20) -- 제품코드 +SL_NM_item DECIMAL -- 판매 수량 +SL_NM_cost_a DECIMAL -- 판매 단가 +SL_TOTAL_PRICE DECIMAL -- 판매 금액 (수량 × 단가) +INPRICE DECIMAL -- 입고가 ⭐⭐⭐ 마진 계산의 핵심! +SL_MY_in_cost DECIMAL -- 입고 원가 (= INPRICE) +SL_INPUT_PRICE DECIMAL -- 입력가격 +BARCODE NVARCHAR(20) -- 판매 시 사용된 바코드 +``` + +--- + +## 3. 제품/바코드 구조 + +### 3.1 코드 체계 + +``` +제품코드 (DrugCode) +├── 전문의약품: 9자리 숫자 (예: 652606580) +├── 일반의약품: 9자리 숫자 +└── 자체등록: LB로 시작 (예: LB000003778) + +바코드 종류 +├── 대표바코드: CD_GOODS.BARCODE +├── 단위바코드: CD_ITEM_UNIT_MEMBER.CD_CD_BARCODE +├── APC 코드: 02/92로 시작하는 13자리 (동물약) +└── 표준코드: CD_BARCODE.BASECODE (EDI 연동용) +``` + +### 3.2 바코드 조회 순서 + +```sql +-- 제품의 바코드 찾기 (우선순위) +1. CD_GOODS.BARCODE -- 대표 바코드 +2. CD_ITEM_UNIT_MEMBER.CD_CD_BARCODE -- 단위 바코드 (낱개/박스) +3. CD_BARCODE.BARCODE -- 표준코드 매핑 +``` + +--- + +## 4. 입고 흐름 + +### 4.1 입고 기록 생성 + +``` +도매상 발주 → 입고 등록 + ↓ +┌─────────────┐ +│ WH_main │ 입고 마스터 생성 +│ (입고번호) │ - 도매상코드 +└─────┬───────┘ - 입고일 + │ + ↓ +┌─────────────┐ +│ WH_sub │ 입고 상세 생성 +│ (품목별) │ - 제품코드 +└─────┬───────┘ - 수량, 단가 ⭐ + │ + ↓ +┌─────────────┐ +│ IM_total │ 재고 증가 +└─────────────┘ +``` + +### 4.2 입고 시 가격 업데이트 + +입고 처리 시 **CD_GOODS.Price** (입고가)가 업데이트될 수 있음: + +```sql +-- 최근 입고 단가로 CD_GOODS.Price 업데이트 (POS 설정에 따름) +UPDATE CD_GOODS +SET Price = (최근 입고 단가) +WHERE DrugCode = ? +``` + +> **중요**: POS 설정에 따라 입고 시 자동 업데이트 여부가 결정됨 + +--- + +## 5. 판매 흐름 + +### 5.1 판매 기록 생성 + +``` +바코드 스캔 → 판매 등록 + ↓ +┌─────────────┐ +│ SALE_MAIN │ 판매 마스터 생성 +│ (거래번호) │ - 고객코드, 날짜 +└─────┬───────┘ - 총액, 원가합계 (SL_MY_sale_cost) + │ + ↓ +┌─────────────┐ +│ SALE_SUB │ 판매 상세 생성 (품목별) +│ │ - 제품코드 +│ │ - 판매가 (SL_NM_cost_a) +│ │ - 입고가 (INPRICE) ⭐ +└─────┬───────┘ + │ + ↓ +┌─────────────┐ +│ IM_total │ 재고 감소 +└─────────────┘ +``` + +### 5.2 판매 시 INPRICE 설정 (핵심!) + +**판매 시 SALE_SUB.INPRICE는 CD_GOODS.Price에서 가져옴** + +```sql +-- 판매 등록 시 (POS 시스템 내부 로직 추정) +INSERT INTO SALE_SUB ( + SL_NO_order, DrugCode, SL_NM_item, SL_NM_cost_a, + INPRICE, -- CD_GOODS.Price 값 사용 + ... +) VALUES ( + @거래번호, @제품코드, @수량, @판매단가, + (SELECT Price FROM CD_GOODS WHERE DrugCode = @제품코드), + ... +) +``` + +> **실제 확인 결과**: SALE_SUB.INPRICE ≈ CD_GOODS.Price (99% 일치) + +--- + +## 6. 마진 계산 로직 + +### 6.1 거래별 마진 계산 + +```sql +-- SALE_MAIN에서 마진 계산 +마진액 = SL_MY_sale (판매액) - SL_MY_sale_cost (원가합계) +마진율 = (판매액 - 원가합계) / 판매액 × 100 + +-- SL_MY_sale_cost 계산 (내부 로직) +SL_MY_sale_cost = SUM(SALE_SUB.INPRICE × SALE_SUB.SL_NM_item) + -- 입고가 × 판매수량의 합계 +``` + +### 6.2 품목별 마진 계산 + +```sql +-- SALE_SUB에서 품목별 마진 +품목_마진 = SL_TOTAL_PRICE - (INPRICE × SL_NM_item) + = 판매금액 - (입고가 × 수량) +``` + +### 6.3 마진 계산 예시 + +``` +거래 20260313000076: +├── 판매액: 64,500원 +├── 원가: 31,650원 ← SALE_SUB.INPRICE 합계 +├── 마진: 32,850원 +└── 마진율: 50.9% +``` + +--- + +## 7. 문제점: 입고 없이 판매 시 마진 0 + +### 7.1 문제 원인 + +**INPRICE = 0이 되는 경우:** + +1. **CD_GOODS.Price가 0 또는 NULL인 경우** + - 제품 등록 시 입고가를 설정하지 않음 + - 입고 기록 없이 제품만 등록 + +2. **POS 시스템 특수 케이스** + - 일부 상황에서 INPRICE가 0으로 설정됨 + - (정확한 조건은 POS 내부 로직에 따름) + +### 7.2 실제 데이터 분석 (2026-03-13 기준) + +``` +최근 1개월 판매 건수: 1,767건 +├── INPRICE > 0: 1,749건 (98.98%) +└── INPRICE = 0: 18건 (1.02%) ← 마진 0 문제 발생! +``` + +### 7.3 INPRICE=0 사례 분석 + +| 제품코드 | 제품명 | CD_GOODS.Price | SALE_SUB.INPRICE | 입고기록 | +|----------|--------|----------------|------------------|----------| +| LB000003658 | 수리팍(제로슈거) | 1,450 | **0** | 있음 | +| LB000003575 | 알파플러스정 | 1 | **0** | 있음 | +| LB000001822 | 헤파토스시럽 | 1,613 | **0** | 있음 | + +> **특이사항**: 입고 기록이 있고 CD_GOODS.Price도 있는데 INPRICE=0인 경우 존재 +> → POS 특수 상황에서 발생 (추가 조사 필요) + +### 7.4 해결 방안 + +#### 방안 1: 제품 등록 시 입고가 필수 입력 +```sql +-- 제품 등록 시 Price 필수 체크 +IF Price IS NULL OR Price = 0 THEN + ERROR '입고가를 입력하세요' +``` + +#### 방안 2: 판매 전 입고 기록 확인 +```sql +-- 판매 시 입고 기록 확인 +IF NOT EXISTS (SELECT 1 FROM WH_sub WHERE DrugCode = @코드) THEN + WARNING '입고 기록 없음. 마진 계산 불가' +``` + +#### 방안 3: INPRICE 자동 채우기 +```sql +-- INPRICE=0인 경우 CD_GOODS.Price로 업데이트 +UPDATE SALE_SUB +SET INPRICE = (SELECT Price FROM CD_GOODS WHERE DrugCode = SALE_SUB.DrugCode) +WHERE INPRICE = 0 +``` + +--- + +## 8. 관련 API 목록 (app.py) + +| 엔드포인트 | 기능 | 사용 테이블 | +|------------|------|-------------| +| `/api/products` | 제품 검색 | CD_GOODS, IM_total, CD_ITEM_UNIT_MEMBER | +| `/api/drugs//purchase-history` | 입고 이력 | WH_main, WH_sub | +| `/api/sales-detail` | 판매 상세 | SALE_SUB, CD_GOODS | +| `/api/usage` | 기간별 사용량 | SALE_SUB, CD_GOODS | +| `/api/rx-usage` | 처방 사용량 | PS_sub_pharm, PS_main | +| `/admin/transaction/` | 거래 상세 | SALE_MAIN, SALE_SUB | + +--- + +## 9. 테이블 관계도 + +``` +PM_DRUG PM_PRES +======== ======== + +CD_GOODS ────────────────────────┐ + │ DrugCode (PK) │ + │ │ + ├── CD_ITEM_UNIT_MEMBER │ + │ (단위바코드) │ + │ │ + ├── CD_BARCODE │ + │ (표준코드 매핑) │ + │ │ + ├── IM_total │ + │ (현재 재고) │ + │ │ + ├── WH_sub ◄─── WH_main │ + │ (입고 상세) (입고 마스터)│ + │ │ + └──────────────────────────────┼──► SALE_SUB ◄─── SALE_MAIN + │ (판매 상세) (판매 마스터) + │ │ + │ │ INPRICE = CD_GOODS.Price + │ │ + └─────────┘ + +마진 계산: + SALE_MAIN.SL_MY_sale_cost = Σ(SALE_SUB.INPRICE × 수량) + 마진 = SL_MY_sale - SL_MY_sale_cost +``` + +--- + +## 10. 핵심 요약 + +### 10.1 마진 계산 흐름 +``` +입고 등록 (WH_sub) + ↓ +CD_GOODS.Price 업데이트 (입고가) + ↓ +판매 등록 (SALE_SUB) + ↓ +SALE_SUB.INPRICE ← CD_GOODS.Price ⭐ + ↓ +SALE_MAIN.SL_MY_sale_cost = Σ(INPRICE × 수량) + ↓ +마진 = 판매액 - 원가 +``` + +### 10.2 문제 원인 +- **INPRICE = 0**이면 마진 = 판매액 (100% 마진처럼 보이지만 실제로는 잘못된 데이터) +- **CD_GOODS.Price = 0**이면 판매 시 INPRICE도 0 + +### 10.3 권장 조치 +1. 제품 등록 시 입고가(Price) 필수 입력 강제 +2. 입고 처리 후 판매 권장 (입고 기록 없으면 경고) +3. 마진 리포트에서 INPRICE=0인 건 별도 표시/경고 + +--- + +*분석: 용림 (Yongrim) | 2026-03-13*