- config.py: PMPLUS20 서버(192.168.0.201\PMPLUS20) 및 SA 비밀번호 수정 - v2_pmplus20.py: PS_MAIN→TBSID040_03, CD_SUNAB→TBSIR000_01 매핑 적용 - 컬럼 매핑: PRICE_T→TOT_PRICE, PRICE_C→INS_PRICE, PRICE_P→EXE_PRICE 등 - Drug_T4 = NON_DRUG_PRICE + EXP_EXE_PRICE - Holiday = HD_ADD + PRES_TIME_GUBUN 조합 - PreGubun = MPRE_TYPE (차상위 F 별도 처리) - PRES_GUBUN='E' 재고보정 레코드 제외 - PMPLUS20_MIGRATION_GUIDE.md: 전체 매핑 가이드 문서 추가 - app.py: 포트 5060→5050 변경 20260324 기준 검증: 건수/금액/보험별/결제별 일치 확인 PRICE_N(수납) 2건 차이(23,420원)는 비급여 수납 처리 개선에 의한 정상 차이 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
309 lines
8.5 KiB
Python
309 lines
8.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Pharmacy Stats API - Flask 서버
|
|
|
|
QT-POS 통계 다이얼로그 API 버전
|
|
v1: PharmIT3000
|
|
v2: PMPLUS20
|
|
"""
|
|
from flask import Flask, jsonify, request, render_template
|
|
from datetime import date, timedelta
|
|
|
|
from queries import v1_pharmit3000 as v1
|
|
from queries import v2_pmplus20 as v2
|
|
|
|
app = Flask(__name__)
|
|
|
|
# 보험구분 라벨
|
|
GUBUN_LABEL = {
|
|
'0': '건강보험', '1': '의료급여', '2': '산재', '3': '자동차',
|
|
'4': '보훈', '5': '공상', '6': '본인', '7': '차상위1',
|
|
'8': '희귀', '9': '비급여', 'E': '차상위2', 'F': '차상위2',
|
|
}
|
|
|
|
|
|
@app.route('/')
|
|
def index():
|
|
return render_template('stats.html')
|
|
|
|
|
|
# ============== v1 API (PharmIT3000) ==============
|
|
|
|
@app.route('/v1/api/stats')
|
|
def v1_stats():
|
|
"""v1 전체 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
try:
|
|
result = v1.get_sales_stats(date_from, date_to)
|
|
result['version'] = 'v1'
|
|
result['source'] = 'PharmIT3000'
|
|
result['date_from'] = date_from
|
|
result['date_to'] = date_to
|
|
return jsonify(result)
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@app.route('/v1/api/stats/insurance')
|
|
def v1_stats_insurance():
|
|
"""v1 보험별 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
result = v1.get_sales_stats(date_from, date_to)
|
|
|
|
by_gubun = []
|
|
for code, data in result.get('by_gubun', {}).items():
|
|
by_gubun.append({
|
|
'code': code,
|
|
'label': GUBUN_LABEL.get(code, code),
|
|
**data
|
|
})
|
|
|
|
return jsonify({
|
|
'version': 'v1',
|
|
'source': 'PharmIT3000',
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'total': result['total'],
|
|
'by_gubun': sorted(by_gubun, key=lambda x: x['code'])
|
|
})
|
|
|
|
|
|
@app.route('/v1/api/stats/time')
|
|
def v1_stats_time():
|
|
"""v1 시간가산별 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
result = v1.get_sales_stats(date_from, date_to)
|
|
|
|
return jsonify({
|
|
'version': 'v1',
|
|
'source': 'PharmIT3000',
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'total': result['total'],
|
|
'by_time': result['by_time']
|
|
})
|
|
|
|
|
|
@app.route('/v1/api/stats/payment')
|
|
def v1_stats_payment():
|
|
"""v1 결제수단별 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
result = v1.get_sales_stats(date_from, date_to)
|
|
|
|
return jsonify({
|
|
'version': 'v1',
|
|
'source': 'PharmIT3000',
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'total': result['total'],
|
|
'by_pay': result['by_pay']
|
|
})
|
|
|
|
|
|
@app.route('/v1/api/stats/hospital')
|
|
def v1_stats_hospital():
|
|
"""v1 병원별 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
result = v1.get_sales_stats(date_from, date_to)
|
|
|
|
by_hosp = []
|
|
for name, data in result.get('by_hosp', {}).items():
|
|
by_hosp.append({'name': name, **data})
|
|
|
|
return jsonify({
|
|
'version': 'v1',
|
|
'source': 'PharmIT3000',
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'total': result['total'],
|
|
'by_hosp': sorted(by_hosp, key=lambda x: -x['cnt'])[:50] # 상위 50개
|
|
})
|
|
|
|
|
|
# ============== v2 API (PMPLUS20) ==============
|
|
|
|
@app.route('/v2/api/stats')
|
|
def v2_stats():
|
|
"""v2 전체 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
try:
|
|
result = v2.get_sales_stats(date_from, date_to)
|
|
result['version'] = 'v2'
|
|
result['source'] = 'PMPLUS20'
|
|
result['date_from'] = date_from
|
|
result['date_to'] = date_to
|
|
return jsonify(result)
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@app.route('/v2/api/stats/insurance')
|
|
def v2_stats_insurance():
|
|
"""v2 보험별 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
result = v2.get_sales_stats(date_from, date_to)
|
|
|
|
if 'error' in result:
|
|
return jsonify(result), 500
|
|
|
|
by_gubun = []
|
|
for code, data in result.get('by_gubun', {}).items():
|
|
by_gubun.append({
|
|
'code': code,
|
|
'label': GUBUN_LABEL.get(code, code),
|
|
**data
|
|
})
|
|
|
|
return jsonify({
|
|
'version': 'v2',
|
|
'source': 'PMPLUS20',
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'total': result['total'],
|
|
'by_gubun': sorted(by_gubun, key=lambda x: x['code'])
|
|
})
|
|
|
|
|
|
@app.route('/v2/api/stats/time')
|
|
def v2_stats_time():
|
|
"""v2 시간가산별 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
result = v2.get_sales_stats(date_from, date_to)
|
|
if 'error' in result:
|
|
return jsonify(result), 500
|
|
|
|
return jsonify({
|
|
'version': 'v2',
|
|
'source': 'PMPLUS20',
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'total': result['total'],
|
|
'by_time': result['by_time']
|
|
})
|
|
|
|
|
|
@app.route('/v2/api/stats/payment')
|
|
def v2_stats_payment():
|
|
"""v2 결제수단별 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
result = v2.get_sales_stats(date_from, date_to)
|
|
if 'error' in result:
|
|
return jsonify(result), 500
|
|
|
|
return jsonify({
|
|
'version': 'v2',
|
|
'source': 'PMPLUS20',
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'total': result['total'],
|
|
'by_pay': result['by_pay']
|
|
})
|
|
|
|
|
|
@app.route('/v2/api/stats/hospital')
|
|
def v2_stats_hospital():
|
|
"""v2 병원별 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
result = v2.get_sales_stats(date_from, date_to)
|
|
if 'error' in result:
|
|
return jsonify(result), 500
|
|
|
|
by_hosp = []
|
|
for name, data in result.get('by_hosp', {}).items():
|
|
by_hosp.append({'name': name, **data})
|
|
|
|
return jsonify({
|
|
'version': 'v2',
|
|
'source': 'PMPLUS20',
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'total': result['total'],
|
|
'by_hosp': sorted(by_hosp, key=lambda x: -x['cnt'])[:50]
|
|
})
|
|
|
|
|
|
# ============== 비교 API ==============
|
|
|
|
@app.route('/api/compare')
|
|
def compare():
|
|
"""v1 vs v2 비교"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
v1_result = v1.get_sales_stats(date_from, date_to)
|
|
v2_result = v2.get_sales_stats(date_from, date_to)
|
|
|
|
# 차이 계산
|
|
diff = {}
|
|
if 'total' in v1_result and 'total' in v2_result:
|
|
v1_total = v1_result['total']
|
|
v2_total = v2_result['total']
|
|
for key in v1_total:
|
|
v1_val = v1_total.get(key, 0)
|
|
v2_val = v2_total.get(key, 0)
|
|
diff[key] = v2_val - v1_val
|
|
|
|
return jsonify({
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'v1': {
|
|
'source': 'PharmIT3000',
|
|
'total': v1_result.get('total', {}),
|
|
'error': v1_result.get('error')
|
|
},
|
|
'v2': {
|
|
'source': 'PMPLUS20',
|
|
'total': v2_result.get('total', {}),
|
|
'error': v2_result.get('error')
|
|
},
|
|
'diff': diff
|
|
})
|
|
|
|
|
|
def _default_from():
|
|
"""기본 시작일: 이번 달 1일"""
|
|
today = date.today()
|
|
return today.replace(day=1).strftime('%Y%m%d')
|
|
|
|
|
|
def _default_to():
|
|
"""기본 종료일: 오늘"""
|
|
return date.today().strftime('%Y%m%d')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
print("🏥 Pharmacy Stats API")
|
|
print("http://0.0.0.0:5060")
|
|
print("")
|
|
print("Endpoints:")
|
|
print(" /v1/api/stats - PharmIT3000 전체 통계")
|
|
print(" /v1/api/stats/insurance - PharmIT3000 보험별")
|
|
print(" /v1/api/stats/time - PharmIT3000 시간가산별")
|
|
print(" /v1/api/stats/payment - PharmIT3000 결제수단별")
|
|
print(" /v1/api/stats/hospital - PharmIT3000 병원별")
|
|
print("")
|
|
print(" /v2/api/stats/... - PMPLUS20 (동일 구조)")
|
|
print("")
|
|
print(" /api/compare - v1 vs v2 비교")
|
|
app.run(host='0.0.0.0', port=5050, debug=True)
|