feat: 반품관리 페이지 추가
This commit is contained in:
parent
09948c234f
commit
d6cf4c2cc1
152
backend/app.py
152
backend/app.py
@ -5234,6 +5234,158 @@ def api_kims_log_detail(log_id):
|
|||||||
return jsonify({'success': False, 'error': str(e)})
|
return jsonify({'success': False, 'error': str(e)})
|
||||||
|
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
# 반품 후보 관리
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
@app.route('/admin/return-management')
|
||||||
|
def admin_return_management():
|
||||||
|
"""반품 후보 관리 페이지"""
|
||||||
|
return render_template('admin_return_management.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/return-candidates')
|
||||||
|
def api_return_candidates():
|
||||||
|
"""반품 후보 목록 조회 API"""
|
||||||
|
status = request.args.get('status', '')
|
||||||
|
urgency = request.args.get('urgency', '')
|
||||||
|
search = request.args.get('search', '')
|
||||||
|
sort = request.args.get('sort', 'months_desc')
|
||||||
|
|
||||||
|
try:
|
||||||
|
db_path = os.path.join(BACKEND_DIR, 'db', 'orders.db')
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
conn.row_factory = sqlite3.Row
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# 기본 쿼리
|
||||||
|
query = "SELECT * FROM return_candidates WHERE 1=1"
|
||||||
|
params = []
|
||||||
|
|
||||||
|
# 상태 필터
|
||||||
|
if status:
|
||||||
|
query += " AND status = ?"
|
||||||
|
params.append(status)
|
||||||
|
|
||||||
|
# 검색어 필터
|
||||||
|
if search:
|
||||||
|
query += " AND (drug_name LIKE ? OR drug_code LIKE ?)"
|
||||||
|
params.extend([f'%{search}%', f'%{search}%'])
|
||||||
|
|
||||||
|
# 긴급도 필터
|
||||||
|
if urgency == 'critical':
|
||||||
|
query += " AND (months_since_use >= 36 OR months_since_purchase >= 36)"
|
||||||
|
elif urgency == 'warning':
|
||||||
|
query += " AND ((months_since_use >= 24 OR months_since_purchase >= 24) AND (COALESCE(months_since_use, 0) < 36 AND COALESCE(months_since_purchase, 0) < 36))"
|
||||||
|
elif urgency == 'normal':
|
||||||
|
query += " AND (COALESCE(months_since_use, 0) < 24 AND COALESCE(months_since_purchase, 0) < 24)"
|
||||||
|
|
||||||
|
# 정렬
|
||||||
|
sort_map = {
|
||||||
|
'months_desc': 'COALESCE(months_since_use, months_since_purchase, 0) DESC',
|
||||||
|
'months_asc': 'COALESCE(months_since_use, months_since_purchase, 0) ASC',
|
||||||
|
'stock_desc': 'current_stock DESC',
|
||||||
|
'name_asc': 'drug_name ASC',
|
||||||
|
'detected_desc': 'detected_at DESC'
|
||||||
|
}
|
||||||
|
query += f" ORDER BY {sort_map.get(sort, 'detected_at DESC')}"
|
||||||
|
|
||||||
|
cursor.execute(query, params)
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
|
||||||
|
items = []
|
||||||
|
for row in rows:
|
||||||
|
items.append({
|
||||||
|
'id': row['id'],
|
||||||
|
'drug_code': row['drug_code'],
|
||||||
|
'drug_name': row['drug_name'],
|
||||||
|
'current_stock': row['current_stock'],
|
||||||
|
'last_prescription_date': row['last_prescription_date'],
|
||||||
|
'months_since_use': row['months_since_use'],
|
||||||
|
'last_purchase_date': row['last_purchase_date'],
|
||||||
|
'months_since_purchase': row['months_since_purchase'],
|
||||||
|
'status': row['status'],
|
||||||
|
'decision_reason': row['decision_reason'],
|
||||||
|
'detected_at': row['detected_at'],
|
||||||
|
'updated_at': row['updated_at']
|
||||||
|
})
|
||||||
|
|
||||||
|
# 통계 계산
|
||||||
|
cursor.execute("SELECT COUNT(*) FROM return_candidates WHERE months_since_use >= 36 OR months_since_purchase >= 36")
|
||||||
|
critical = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
cursor.execute("""SELECT COUNT(*) FROM return_candidates
|
||||||
|
WHERE (months_since_use >= 24 OR months_since_purchase >= 24)
|
||||||
|
AND (COALESCE(months_since_use, 0) < 36 AND COALESCE(months_since_purchase, 0) < 36)""")
|
||||||
|
warning = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
cursor.execute("SELECT COUNT(*) FROM return_candidates WHERE status = 'pending'")
|
||||||
|
pending = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
cursor.execute("SELECT COUNT(*) FROM return_candidates WHERE status IN ('returned', 'keep', 'disposed', 'resolved')")
|
||||||
|
processed = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
cursor.execute("SELECT COUNT(*) FROM return_candidates")
|
||||||
|
total = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'success': True,
|
||||||
|
'items': items,
|
||||||
|
'stats': {
|
||||||
|
'critical': critical,
|
||||||
|
'warning': warning,
|
||||||
|
'pending': pending,
|
||||||
|
'processed': processed,
|
||||||
|
'total': total
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'success': False, 'error': str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/return-candidates/<int:item_id>', methods=['PUT'])
|
||||||
|
def api_update_return_candidate(item_id):
|
||||||
|
"""반품 후보 상태 변경 API"""
|
||||||
|
try:
|
||||||
|
data = request.get_json()
|
||||||
|
new_status = data.get('status')
|
||||||
|
reason = data.get('reason', '')
|
||||||
|
|
||||||
|
if not new_status:
|
||||||
|
return jsonify({'success': False, 'error': '상태가 지정되지 않았습니다'}), 400
|
||||||
|
|
||||||
|
valid_statuses = ['pending', 'reviewed', 'returned', 'keep', 'disposed', 'resolved']
|
||||||
|
if new_status not in valid_statuses:
|
||||||
|
return jsonify({'success': False, 'error': f'유효하지 않은 상태: {new_status}'}), 400
|
||||||
|
|
||||||
|
db_path = os.path.join(BACKEND_DIR, 'db', 'orders.db')
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
cursor.execute("""
|
||||||
|
UPDATE return_candidates
|
||||||
|
SET status = ?,
|
||||||
|
decision_reason = ?,
|
||||||
|
reviewed_at = datetime('now', 'localtime'),
|
||||||
|
updated_at = datetime('now', 'localtime')
|
||||||
|
WHERE id = ?
|
||||||
|
""", (new_status, reason, item_id))
|
||||||
|
|
||||||
|
if cursor.rowcount == 0:
|
||||||
|
conn.close()
|
||||||
|
return jsonify({'success': False, 'error': '항목을 찾을 수 없습니다'}), 404
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return jsonify({'success': True, 'message': '상태가 변경되었습니다'})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'success': False, 'error': str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/kims/interaction-check', methods=['POST'])
|
@app.route('/api/kims/interaction-check', methods=['POST'])
|
||||||
def api_kims_interaction_check():
|
def api_kims_interaction_check():
|
||||||
"""
|
"""
|
||||||
|
|||||||
1130
backend/templates/admin_return_management.html
Normal file
1130
backend/templates/admin_return_management.html
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user