feat: 도매상 잔고 모달에 월간 매출 추가
- 백제/지오영/수인 월간매출 API 라우트 추가 - 모달 UI: 잔고 + 월간 매출 동시 표시 - 총 주문액 / 총 미수금 요약 표시
This commit is contained in:
parent
4b2d934839
commit
5519f5ae62
86
backend/analyze_baekje_ledger.py
Normal file
86
backend/analyze_baekje_ledger.py
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""백제약품 주문 원장 페이지 분석"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
async def analyze_order_ledger():
|
||||||
|
from playwright.async_api import async_playwright
|
||||||
|
|
||||||
|
username = os.getenv('BAEKJE_USER_ID')
|
||||||
|
password = os.getenv('BAEKJE_PASSWORD')
|
||||||
|
|
||||||
|
print(f'Username: {username}')
|
||||||
|
|
||||||
|
async with async_playwright() as p:
|
||||||
|
browser = await p.chromium.launch(headless=False)
|
||||||
|
context = await browser.new_context()
|
||||||
|
page = await context.new_page()
|
||||||
|
|
||||||
|
# 로그인 페이지
|
||||||
|
await page.goto('https://ibjp.co.kr/dist/login', timeout=15000)
|
||||||
|
await page.wait_for_load_state('networkidle', timeout=10000)
|
||||||
|
|
||||||
|
# 로그인 폼 입력
|
||||||
|
inputs = await page.locator('input[type="text"], input[type="password"]').all()
|
||||||
|
if len(inputs) >= 2:
|
||||||
|
await inputs[0].fill(username)
|
||||||
|
await inputs[1].fill(password)
|
||||||
|
|
||||||
|
# 로그인 버튼 클릭
|
||||||
|
buttons = await page.locator('button').all()
|
||||||
|
for btn in buttons:
|
||||||
|
text = await btn.text_content()
|
||||||
|
if '로그인' in (text or ''):
|
||||||
|
await btn.click()
|
||||||
|
break
|
||||||
|
|
||||||
|
# 로그인 완료 대기
|
||||||
|
try:
|
||||||
|
await page.wait_for_url('**/comOrd**', timeout=15000)
|
||||||
|
print('Login successful, redirected to comOrd')
|
||||||
|
except Exception as e:
|
||||||
|
print(f'URL wait failed: {e}')
|
||||||
|
await asyncio.sleep(3)
|
||||||
|
|
||||||
|
print(f'Current URL: {page.url}')
|
||||||
|
|
||||||
|
# 주문 원장 페이지로 이동
|
||||||
|
await page.goto('https://ibjp.co.kr/dist/ordLedger', timeout=15000)
|
||||||
|
await page.wait_for_load_state('networkidle', timeout=15000)
|
||||||
|
|
||||||
|
print(f'Order Ledger URL: {page.url}')
|
||||||
|
|
||||||
|
# 페이지 HTML 저장
|
||||||
|
html = await page.content()
|
||||||
|
with open('ordLedger_page.html', 'w', encoding='utf-8') as f:
|
||||||
|
f.write(html)
|
||||||
|
print('Page HTML saved to ordLedger_page.html')
|
||||||
|
|
||||||
|
# 스크린샷 저장
|
||||||
|
await page.screenshot(path='ordLedger_screenshot.png', full_page=True)
|
||||||
|
print('Screenshot saved')
|
||||||
|
|
||||||
|
# 테이블 데이터 분석
|
||||||
|
tables = await page.locator('table').all()
|
||||||
|
print(f'Found {len(tables)} tables')
|
||||||
|
|
||||||
|
for i, table in enumerate(tables):
|
||||||
|
headers = await table.locator('th').all()
|
||||||
|
header_texts = [await h.text_content() for h in headers]
|
||||||
|
print(f'Table {i} headers: {header_texts}')
|
||||||
|
|
||||||
|
# 페이지 텍스트 출력 (분석용)
|
||||||
|
body_text = await page.locator('body').text_content()
|
||||||
|
print('\n=== Page Text Preview ===')
|
||||||
|
print(body_text[:3000] if body_text else 'No text')
|
||||||
|
|
||||||
|
await asyncio.sleep(30) # 페이지 확인 시간
|
||||||
|
await browser.close()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
asyncio.run(analyze_order_ledger())
|
||||||
@ -447,6 +447,37 @@ def api_get_balance():
|
|||||||
}), 501
|
}), 501
|
||||||
|
|
||||||
|
|
||||||
|
@geoyoung_bp.route('/monthly-sales', methods=['GET'])
|
||||||
|
def api_get_monthly_sales():
|
||||||
|
"""
|
||||||
|
월간 매출 조회
|
||||||
|
|
||||||
|
GET /api/geoyoung/monthly-sales?year=2026&month=3
|
||||||
|
"""
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
year = request.args.get('year', type=int)
|
||||||
|
month = request.args.get('month', type=int)
|
||||||
|
|
||||||
|
# 기본값: 현재 월
|
||||||
|
if not year or not month:
|
||||||
|
now = datetime.now()
|
||||||
|
year = year or now.year
|
||||||
|
month = month or now.month
|
||||||
|
|
||||||
|
session = get_geo_session()
|
||||||
|
|
||||||
|
if hasattr(session, 'get_monthly_sales'):
|
||||||
|
result = session.get_monthly_sales(year, month)
|
||||||
|
return jsonify(result)
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'NOT_IMPLEMENTED',
|
||||||
|
'message': '지오영 월간 매출 조회 미구현'
|
||||||
|
}), 501
|
||||||
|
|
||||||
|
|
||||||
# ========== 하위 호환성 ==========
|
# ========== 하위 호환성 ==========
|
||||||
|
|
||||||
# 기존 코드에서 직접 클래스 참조하는 경우를 위해
|
# 기존 코드에서 직접 클래스 참조하는 경우를 위해
|
||||||
|
|||||||
10
backend/ordLedger_page.html
Normal file
10
backend/ordLedger_page.html
Normal file
File diff suppressed because one or more lines are too long
BIN
backend/ordLedger_screenshot.png
Normal file
BIN
backend/ordLedger_screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 302 KiB |
@ -134,6 +134,55 @@ def api_sooin_balance():
|
|||||||
}), 500
|
}), 500
|
||||||
|
|
||||||
|
|
||||||
|
@sooin_bp.route('/monthly-sales', methods=['GET'])
|
||||||
|
def api_sooin_monthly_sales():
|
||||||
|
"""
|
||||||
|
수인약품 월간 매출 조회 API
|
||||||
|
|
||||||
|
GET /api/sooin/monthly-sales?year=2026&month=3
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"total_amount": 3700239, // 월간 매출 합계
|
||||||
|
"total_paid": 0, // 월간 입금 합계
|
||||||
|
"ending_balance": 14293001, // 월말 잔액
|
||||||
|
"opening_balance": 10592762, // 전일(기초) 잔액
|
||||||
|
"from_date": "2026-03-01",
|
||||||
|
"to_date": "2026-03-31"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
year = flask_request.args.get('year', type=int)
|
||||||
|
month = flask_request.args.get('month', type=int)
|
||||||
|
|
||||||
|
# 기본값: 현재 월
|
||||||
|
if not year or not month:
|
||||||
|
now = datetime.now()
|
||||||
|
year = year or now.year
|
||||||
|
month = month or now.month
|
||||||
|
|
||||||
|
try:
|
||||||
|
session = get_sooin_session()
|
||||||
|
if hasattr(session, 'get_monthly_sales'):
|
||||||
|
result = session.get_monthly_sales(year, month)
|
||||||
|
return jsonify(result)
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'NOT_IMPLEMENTED',
|
||||||
|
'message': '수인약품 월간 매출 조회 미구현'
|
||||||
|
}), 501
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"수인약품 월간 매출 조회 오류: {e}")
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'MONTHLY_SALES_ERROR',
|
||||||
|
'message': str(e)
|
||||||
|
}), 500
|
||||||
|
|
||||||
|
|
||||||
@sooin_bp.route('/cart', methods=['GET'])
|
@sooin_bp.route('/cart', methods=['GET'])
|
||||||
def api_sooin_cart():
|
def api_sooin_cart():
|
||||||
"""장바구니 조회 API"""
|
"""장바구니 조회 API"""
|
||||||
|
|||||||
@ -2256,26 +2256,43 @@
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
}
|
}
|
||||||
.balance-total {
|
.balance-summary {
|
||||||
background: linear-gradient(135deg, rgba(168, 85, 247, 0.1), rgba(6, 182, 212, 0.1));
|
background: linear-gradient(135deg, rgba(168, 85, 247, 0.1), rgba(6, 182, 212, 0.1));
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
text-align: center;
|
display: flex;
|
||||||
margin-top: 8px;
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 12px;
|
||||||
|
gap: 16px;
|
||||||
}
|
}
|
||||||
.balance-total-label {
|
.summary-item {
|
||||||
|
text-align: center;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.summary-label {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
.balance-total-value {
|
.summary-value {
|
||||||
font-size: 28px;
|
font-size: 24px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-family: 'JetBrains Mono', monospace;
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
}
|
||||||
|
.summary-value.sales {
|
||||||
|
color: #10b981;
|
||||||
|
}
|
||||||
|
.summary-value.balance {
|
||||||
background: linear-gradient(135deg, #a855f7, #06b6d4);
|
background: linear-gradient(135deg, #a855f7, #06b6d4);
|
||||||
-webkit-background-clip: text;
|
-webkit-background-clip: text;
|
||||||
-webkit-text-fill-color: transparent;
|
-webkit-text-fill-color: transparent;
|
||||||
}
|
}
|
||||||
|
.summary-divider {
|
||||||
|
width: 1px;
|
||||||
|
height: 50px;
|
||||||
|
background: var(--border);
|
||||||
|
}
|
||||||
.balance-updated {
|
.balance-updated {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
@ -2300,17 +2317,23 @@
|
|||||||
baekje: {
|
baekje: {
|
||||||
id: 'baekje', name: '백제약품', icon: '💉',
|
id: 'baekje', name: '백제약품', icon: '💉',
|
||||||
logo: '/static/img/logo_baekje.svg',
|
logo: '/static/img/logo_baekje.svg',
|
||||||
color: '#f59e0b', api: '/api/baekje/balance'
|
color: '#f59e0b',
|
||||||
|
balanceApi: '/api/baekje/balance',
|
||||||
|
salesApi: '/api/baekje/monthly-sales'
|
||||||
},
|
},
|
||||||
geoyoung: {
|
geoyoung: {
|
||||||
id: 'geoyoung', name: '지오영', icon: '🏭',
|
id: 'geoyoung', name: '지오영', icon: '🏭',
|
||||||
logo: '/static/img/logo_geoyoung.ico',
|
logo: '/static/img/logo_geoyoung.ico',
|
||||||
color: '#06b6d4', api: '/api/geoyoung/balance'
|
color: '#06b6d4',
|
||||||
|
balanceApi: '/api/geoyoung/balance',
|
||||||
|
salesApi: '/api/geoyoung/monthly-sales'
|
||||||
},
|
},
|
||||||
sooin: {
|
sooin: {
|
||||||
id: 'sooin', name: '수인약품', icon: '💊',
|
id: 'sooin', name: '수인약품', icon: '💊',
|
||||||
logo: '/static/img/logo_sooin.svg',
|
logo: '/static/img/logo_sooin.svg',
|
||||||
color: '#a855f7', api: '/api/sooin/balance'
|
color: '#a855f7',
|
||||||
|
balanceApi: '/api/sooin/balance',
|
||||||
|
salesApi: '/api/sooin/monthly-sales'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const WHOLESALER_ORDER = ['baekje', 'geoyoung', 'sooin'];
|
const WHOLESALER_ORDER = ['baekje', 'geoyoung', 'sooin'];
|
||||||
@ -2320,36 +2343,47 @@
|
|||||||
content.innerHTML = `
|
content.innerHTML = `
|
||||||
<div class="loading-state">
|
<div class="loading-state">
|
||||||
<div class="loading-spinner"></div>
|
<div class="loading-spinner"></div>
|
||||||
<div>잔고 조회 중...</div>
|
<div>잔고 및 매출 조회 중...</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
const wholesalers = WHOLESALER_ORDER.map(id => WHOLESALER_CONFIG[id]);
|
const wholesalers = WHOLESALER_ORDER.map(id => WHOLESALER_CONFIG[id]);
|
||||||
|
const now = new Date();
|
||||||
|
const year = now.getFullYear();
|
||||||
|
const month = now.getMonth() + 1;
|
||||||
|
|
||||||
const results = {};
|
const balanceResults = {};
|
||||||
|
const salesResults = {};
|
||||||
let totalBalance = 0;
|
let totalBalance = 0;
|
||||||
|
let totalSales = 0;
|
||||||
|
|
||||||
// 병렬로 조회
|
// 병렬로 잔고 + 월간매출 조회
|
||||||
await Promise.all(wholesalers.map(async (ws) => {
|
await Promise.all(wholesalers.flatMap(ws => [
|
||||||
try {
|
// 잔고 조회
|
||||||
const resp = await fetch(ws.api, { timeout: 30000 });
|
fetch(ws.balanceApi).then(r => r.json()).then(data => {
|
||||||
const data = await resp.json();
|
balanceResults[ws.id] = data;
|
||||||
results[ws.id] = data;
|
if (data.success && data.balance) totalBalance += data.balance;
|
||||||
if (data.success && data.balance) {
|
}).catch(err => {
|
||||||
totalBalance += data.balance;
|
balanceResults[ws.id] = { success: false, error: err.message };
|
||||||
}
|
}),
|
||||||
} catch (err) {
|
// 월간 매출 조회
|
||||||
results[ws.id] = { success: false, error: err.message };
|
fetch(`${ws.salesApi}?year=${year}&month=${month}`).then(r => r.json()).then(data => {
|
||||||
}
|
salesResults[ws.id] = data;
|
||||||
}));
|
if (data.success && data.total_amount) totalSales += data.total_amount;
|
||||||
|
}).catch(err => {
|
||||||
|
salesResults[ws.id] = { success: false, error: err.message };
|
||||||
|
})
|
||||||
|
]));
|
||||||
|
|
||||||
// 결과 렌더링
|
// 결과 렌더링
|
||||||
let html = '<div class="balance-grid">';
|
let html = '<div class="balance-grid">';
|
||||||
|
|
||||||
wholesalers.forEach(ws => {
|
wholesalers.forEach(ws => {
|
||||||
const data = results[ws.id];
|
const balData = balanceResults[ws.id] || {};
|
||||||
const isError = !data.success;
|
const salesData = salesResults[ws.id] || {};
|
||||||
const balance = data.balance || 0;
|
const isError = !balData.success;
|
||||||
const prevBalance = data.prev_balance || data.prev_month_balance || 0;
|
const balance = balData.balance || 0;
|
||||||
|
const monthlySales = salesData.total_amount || 0;
|
||||||
|
const monthlyPaid = salesData.total_paid || 0;
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
<div class="balance-card ${isError ? 'error' : ''}">
|
<div class="balance-card ${isError ? 'error' : ''}">
|
||||||
@ -2362,8 +2396,9 @@
|
|||||||
<div class="balance-name">${ws.name}</div>
|
<div class="balance-name">${ws.name}</div>
|
||||||
<div class="balance-detail">
|
<div class="balance-detail">
|
||||||
${isError
|
${isError
|
||||||
? `❌ ${data.error || '조회 실패'}`
|
? `❌ ${balData.error || '조회 실패'}`
|
||||||
: `전월/전일: ${prevBalance.toLocaleString()}원`}
|
: `${month}월 매출: <span style="color:${ws.color};font-weight:600;">${monthlySales.toLocaleString()}원</span>
|
||||||
|
${monthlyPaid > 0 ? ` · 입금: ${monthlyPaid.toLocaleString()}원` : ''}`}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="balance-amount">
|
<div class="balance-amount">
|
||||||
@ -2376,9 +2411,16 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
<div class="balance-total">
|
<div class="balance-summary">
|
||||||
<div class="balance-total-label">총 미수금</div>
|
<div class="summary-item">
|
||||||
<div class="balance-total-value">${totalBalance.toLocaleString()}원</div>
|
<div class="summary-label">📊 ${month}월 총 주문</div>
|
||||||
|
<div class="summary-value sales">${totalSales.toLocaleString()}원</div>
|
||||||
|
</div>
|
||||||
|
<div class="summary-divider"></div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<div class="summary-label">💰 총 미수금</div>
|
||||||
|
<div class="summary-value balance">${totalBalance.toLocaleString()}원</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="balance-updated">🕐 ${new Date().toLocaleString('ko-KR')}</div>
|
<div class="balance-updated">🕐 ${new Date().toLocaleString('ko-KR')}</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|||||||
101
backend/test_baekje_ledger_api.py
Normal file
101
backend/test_baekje_ledger_api.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""백제약품 주문 원장 API 분석"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import calendar
|
||||||
|
|
||||||
|
# 저장된 토큰 로드
|
||||||
|
TOKEN_FILE = r'c:\Users\청춘약국\source\pharmacy-wholesale-api\.baekje_token.json'
|
||||||
|
with open(TOKEN_FILE, 'r', encoding='utf-8') as f:
|
||||||
|
token_data = json.load(f)
|
||||||
|
|
||||||
|
token = token_data['token']
|
||||||
|
cust_cd = token_data['cust_cd']
|
||||||
|
|
||||||
|
print(f"Token expires: {datetime.fromtimestamp(token_data['expires'])}")
|
||||||
|
print(f"Customer code: {cust_cd}")
|
||||||
|
|
||||||
|
# API 세션 설정
|
||||||
|
session = requests.Session()
|
||||||
|
session.headers.update({
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
||||||
|
'Accept': 'application/json, text/plain, */*',
|
||||||
|
'Accept-Language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
|
||||||
|
'Origin': 'https://ibjp.co.kr',
|
||||||
|
'Referer': 'https://ibjp.co.kr/',
|
||||||
|
'Authorization': f'Bearer {token}'
|
||||||
|
})
|
||||||
|
|
||||||
|
API_URL = "https://www.ibjp.co.kr"
|
||||||
|
|
||||||
|
# 1. 주문 원장 API 시도 - 다양한 엔드포인트
|
||||||
|
endpoints = [
|
||||||
|
'/ordLedger/listSearch',
|
||||||
|
'/ordLedger/list',
|
||||||
|
'/ord/ledgerList',
|
||||||
|
'/ord/ledgerSearch',
|
||||||
|
'/cust/ordLedger',
|
||||||
|
'/custOrd/ledgerList',
|
||||||
|
'/ordHist/listSearch',
|
||||||
|
'/ordHist/list',
|
||||||
|
]
|
||||||
|
|
||||||
|
# 날짜 설정 (이번 달)
|
||||||
|
today = datetime.now()
|
||||||
|
year = today.year
|
||||||
|
month = today.month
|
||||||
|
_, last_day = calendar.monthrange(year, month)
|
||||||
|
from_date = f"{year}{month:02d}01"
|
||||||
|
to_date = f"{year}{month:02d}{last_day:02d}"
|
||||||
|
|
||||||
|
print(f"\n조회 기간: {from_date} ~ {to_date}")
|
||||||
|
print("\n=== API 엔드포인트 탐색 ===\n")
|
||||||
|
|
||||||
|
params = {
|
||||||
|
'custCd': cust_cd,
|
||||||
|
'startDt': from_date,
|
||||||
|
'endDt': to_date,
|
||||||
|
'stDate': from_date,
|
||||||
|
'edDate': to_date,
|
||||||
|
'year': str(year),
|
||||||
|
'month': f"{month:02d}",
|
||||||
|
}
|
||||||
|
|
||||||
|
for endpoint in endpoints:
|
||||||
|
try:
|
||||||
|
# GET 시도
|
||||||
|
resp = session.get(f"{API_URL}{endpoint}", params=params, timeout=10)
|
||||||
|
print(f"GET {endpoint}: {resp.status_code}")
|
||||||
|
if resp.status_code == 200:
|
||||||
|
try:
|
||||||
|
data = resp.json()
|
||||||
|
print(f" -> JSON Response (first 500 chars): {str(data)[:500]}")
|
||||||
|
except:
|
||||||
|
print(f" -> Text (first 200 chars): {resp.text[:200]}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"GET {endpoint}: Error - {e}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# POST 시도
|
||||||
|
resp = session.post(f"{API_URL}{endpoint}", json=params, timeout=10)
|
||||||
|
print(f"POST {endpoint}: {resp.status_code}")
|
||||||
|
if resp.status_code == 200:
|
||||||
|
try:
|
||||||
|
data = resp.json()
|
||||||
|
print(f" -> JSON Response (first 500 chars): {str(data)[:500]}")
|
||||||
|
except:
|
||||||
|
print(f" -> Text (first 200 chars): {resp.text[:200]}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"POST {endpoint}: Error - {e}")
|
||||||
|
|
||||||
|
# 2. 이미 알려진 API로 데이터 확인
|
||||||
|
print("\n=== 알려진 API 테스트 ===\n")
|
||||||
|
|
||||||
|
# 월간 잔고 조회 (이미 있는 함수에서 사용)
|
||||||
|
resp = session.get(f"{API_URL}/custMonth/listSearch", params={'custCd': cust_cd, 'year': str(year), 'endDt': to_date}, timeout=10)
|
||||||
|
print(f"custMonth/listSearch: {resp.status_code}")
|
||||||
|
if resp.status_code == 200:
|
||||||
|
data = resp.json()
|
||||||
|
print(f" -> {json.dumps(data, ensure_ascii=False, indent=2)[:1500]}")
|
||||||
126
backend/test_baekje_ledger_api2.py
Normal file
126
backend/test_baekje_ledger_api2.py
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""백제약품 주문 원장 API 분석 - 상세 탐색"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
from datetime import datetime
|
||||||
|
import calendar
|
||||||
|
|
||||||
|
# 저장된 토큰 로드
|
||||||
|
TOKEN_FILE = r'c:\Users\청춘약국\source\pharmacy-wholesale-api\.baekje_token.json'
|
||||||
|
with open(TOKEN_FILE, 'r', encoding='utf-8') as f:
|
||||||
|
token_data = json.load(f)
|
||||||
|
|
||||||
|
token = token_data['token']
|
||||||
|
cust_cd = token_data['cust_cd']
|
||||||
|
|
||||||
|
# API 세션 설정
|
||||||
|
session = requests.Session()
|
||||||
|
session.headers.update({
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
||||||
|
'Accept': 'application/json, text/plain, */*',
|
||||||
|
'Accept-Language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
|
||||||
|
'Origin': 'https://ibjp.co.kr',
|
||||||
|
'Referer': 'https://ibjp.co.kr/',
|
||||||
|
'Authorization': f'Bearer {token}'
|
||||||
|
})
|
||||||
|
|
||||||
|
API_URL = "https://www.ibjp.co.kr"
|
||||||
|
|
||||||
|
today = datetime.now()
|
||||||
|
year = today.year
|
||||||
|
month = today.month
|
||||||
|
_, last_day = calendar.monthrange(year, month)
|
||||||
|
|
||||||
|
print("=== 주문 원장 API 탐색 (다양한 파라미터) ===\n")
|
||||||
|
|
||||||
|
# 날짜 형식 변형
|
||||||
|
date_formats = [
|
||||||
|
{'startDt': f'{year}{month:02d}01', 'endDt': f'{year}{month:02d}{last_day:02d}'},
|
||||||
|
{'stDt': f'{year}{month:02d}01', 'edDt': f'{year}{month:02d}{last_day:02d}'},
|
||||||
|
{'fromDate': f'{year}-{month:02d}-01', 'toDate': f'{year}-{month:02d}-{last_day:02d}'},
|
||||||
|
{'strDt': f'{year}{month:02d}01', 'endDt': f'{year}{month:02d}{last_day:02d}'},
|
||||||
|
{'ordDt': f'{year}{month:02d}'},
|
||||||
|
]
|
||||||
|
|
||||||
|
endpoints = [
|
||||||
|
'/ordLedger/listSearch',
|
||||||
|
'/ordLedger/search',
|
||||||
|
'/ordLedger/ledgerList',
|
||||||
|
'/cust/ordLedgerList',
|
||||||
|
'/cust/ledger',
|
||||||
|
'/ord/histList',
|
||||||
|
'/ord/history',
|
||||||
|
'/ord/list',
|
||||||
|
]
|
||||||
|
|
||||||
|
for endpoint in endpoints:
|
||||||
|
for params in date_formats:
|
||||||
|
full_params = {**params, 'custCd': cust_cd}
|
||||||
|
try:
|
||||||
|
resp = session.get(f"{API_URL}{endpoint}", params=full_params, timeout=10)
|
||||||
|
if resp.status_code == 200:
|
||||||
|
print(f"✓ GET {endpoint} {params}: {resp.status_code}")
|
||||||
|
try:
|
||||||
|
data = resp.json()
|
||||||
|
print(f" -> {str(data)[:300]}")
|
||||||
|
except:
|
||||||
|
print(f" -> {resp.text[:200]}")
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
resp = session.post(f"{API_URL}{endpoint}", json=full_params, timeout=10)
|
||||||
|
if resp.status_code == 200:
|
||||||
|
print(f"✓ POST {endpoint} {params}: {resp.status_code}")
|
||||||
|
try:
|
||||||
|
data = resp.json()
|
||||||
|
print(f" -> {str(data)[:300]}")
|
||||||
|
except:
|
||||||
|
print(f" -> {resp.text[:200]}")
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print("\n=== 주문 이력 관련 API ===\n")
|
||||||
|
|
||||||
|
# 주문 이력 조회 시도
|
||||||
|
order_endpoints = [
|
||||||
|
'/ord/ordList',
|
||||||
|
'/ord/orderHistory',
|
||||||
|
'/ordReg/list',
|
||||||
|
'/ordReg/history',
|
||||||
|
'/order/list',
|
||||||
|
'/order/history',
|
||||||
|
]
|
||||||
|
|
||||||
|
for endpoint in order_endpoints:
|
||||||
|
try:
|
||||||
|
params = {'custCd': cust_cd, 'startDt': f'{year}{month:02d}01', 'endDt': f'{year}{month:02d}{last_day:02d}'}
|
||||||
|
resp = session.get(f"{API_URL}{endpoint}", params=params, timeout=10)
|
||||||
|
print(f"GET {endpoint}: {resp.status_code}")
|
||||||
|
if resp.status_code == 200:
|
||||||
|
try:
|
||||||
|
data = resp.json()
|
||||||
|
print(f" -> {str(data)[:500]}")
|
||||||
|
except:
|
||||||
|
print(f" -> {resp.text[:200]}")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print("\n=== custMonth/listSearch 상세 데이터 분석 ===\n")
|
||||||
|
|
||||||
|
# 이미 작동하는 API의 데이터 상세 분석
|
||||||
|
resp = session.get(f"{API_URL}/custMonth/listSearch", params={'custCd': cust_cd, 'year': str(year), 'endDt': f'{year}{month:02d}{last_day:02d}'}, timeout=10)
|
||||||
|
if resp.status_code == 200:
|
||||||
|
data = resp.json()
|
||||||
|
print("월간 데이터 구조:")
|
||||||
|
for item in data:
|
||||||
|
print(f"\n월: {item.get('BALANCE_YM')}")
|
||||||
|
print(f" 매출액(SALE_AMT): {item.get('SALE_AMT'):,}")
|
||||||
|
print(f" 반품액(BACK_AMT): {item.get('BACK_AMT'):,}")
|
||||||
|
print(f" 순반품(PURE_BACK_AMT): {item.get('PURE_BACK_AMT'):,}")
|
||||||
|
print(f" 순매출(TOTAL_AMT): {item.get('TOTAL_AMT'):,}")
|
||||||
|
print(f" 입금액(PAY_CASH_AMT): {item.get('PAY_CASH_AMT'):,}")
|
||||||
|
print(f" 전월이월(PRE_TOTAL_AMT): {item.get('PRE_TOTAL_AMT'):,}")
|
||||||
|
print(f" 월말잔고(BALANCE_A_AMT): {item.get('BALANCE_A_AMT'):,}")
|
||||||
|
print(f" 회전일수(ROTATE_DAY): {item.get('ROTATE_DAY')}")
|
||||||
84
backend/test_baekje_monthly_sales.py
Normal file
84
backend/test_baekje_monthly_sales.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""백제약품 get_monthly_sales() 테스트"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# wholesale 패키지 경로 추가
|
||||||
|
sys.path.insert(0, r'c:\Users\청춘약국\source\pharmacy-wholesale-api')
|
||||||
|
os.chdir(r'c:\Users\청춘약국\source\pharmacy-pos-qr-system\backend')
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
from wholesale import BaekjeSession
|
||||||
|
|
||||||
|
def test_monthly_sales():
|
||||||
|
print("=" * 60)
|
||||||
|
print("백제약품 월간 매출 조회 테스트")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
session = BaekjeSession()
|
||||||
|
|
||||||
|
# 현재 월 조회
|
||||||
|
from datetime import datetime
|
||||||
|
now = datetime.now()
|
||||||
|
year = now.year
|
||||||
|
month = now.month
|
||||||
|
|
||||||
|
print(f"\n1. 현재 월 ({year}-{month:02d}) 조회:")
|
||||||
|
result = session.get_monthly_sales(year, month)
|
||||||
|
print(f" Success: {result.get('success')}")
|
||||||
|
if result.get('success'):
|
||||||
|
print(f" 월간 매출: {result.get('total_amount'):,}원")
|
||||||
|
print(f" 월간 반품: {result.get('total_returns'):,}원")
|
||||||
|
print(f" 순매출: {result.get('net_amount'):,}원")
|
||||||
|
print(f" 월간 입금: {result.get('total_paid'):,}원")
|
||||||
|
print(f" 월말 잔고: {result.get('ending_balance'):,}원")
|
||||||
|
print(f" 전월이월: {result.get('prev_balance'):,}원")
|
||||||
|
print(f" 회전일수: {result.get('rotate_days')}")
|
||||||
|
print(f" 조회기간: {result.get('from_date')} ~ {result.get('to_date')}")
|
||||||
|
else:
|
||||||
|
print(f" Error: {result.get('error')}")
|
||||||
|
|
||||||
|
# 전월 조회
|
||||||
|
prev_month = month - 1 if month > 1 else 12
|
||||||
|
prev_year = year if month > 1 else year - 1
|
||||||
|
|
||||||
|
print(f"\n2. 전월 ({prev_year}-{prev_month:02d}) 조회:")
|
||||||
|
result = session.get_monthly_sales(prev_year, prev_month)
|
||||||
|
print(f" Success: {result.get('success')}")
|
||||||
|
if result.get('success'):
|
||||||
|
print(f" 월간 매출: {result.get('total_amount'):,}원")
|
||||||
|
print(f" 월간 반품: {result.get('total_returns'):,}원")
|
||||||
|
print(f" 순매출: {result.get('net_amount'):,}원")
|
||||||
|
print(f" 월간 입금: {result.get('total_paid'):,}원")
|
||||||
|
print(f" 월말 잔고: {result.get('ending_balance'):,}원")
|
||||||
|
print(f" 전월이월: {result.get('prev_balance'):,}원")
|
||||||
|
print(f" 회전일수: {result.get('rotate_days')}")
|
||||||
|
print(f" 조회기간: {result.get('from_date')} ~ {result.get('to_date')}")
|
||||||
|
else:
|
||||||
|
print(f" Error: {result.get('error')}")
|
||||||
|
|
||||||
|
# 2달 전 조회
|
||||||
|
prev_month2 = prev_month - 1 if prev_month > 1 else 12
|
||||||
|
prev_year2 = prev_year if prev_month > 1 else prev_year - 1
|
||||||
|
|
||||||
|
print(f"\n3. 2달 전 ({prev_year2}-{prev_month2:02d}) 조회:")
|
||||||
|
result = session.get_monthly_sales(prev_year2, prev_month2)
|
||||||
|
print(f" Success: {result.get('success')}")
|
||||||
|
if result.get('success'):
|
||||||
|
print(f" 월간 매출: {result.get('total_amount'):,}원")
|
||||||
|
print(f" 월간 반품: {result.get('total_returns'):,}원")
|
||||||
|
print(f" 순매출: {result.get('net_amount'):,}원")
|
||||||
|
print(f" 월간 입금: {result.get('total_paid'):,}원")
|
||||||
|
print(f" 월말 잔고: {result.get('ending_balance'):,}원")
|
||||||
|
else:
|
||||||
|
print(f" Error: {result.get('error')}")
|
||||||
|
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("테스트 완료!")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test_monthly_sales()
|
||||||
Loading…
Reference in New Issue
Block a user