Files
waiting-system/routers/franchise.py
Jun-dev f699a29a85 Add waiting system application files
- Add main application files (main.py, models.py, schemas.py, etc.)
- Add routers for all features (waiting, attendance, members, etc.)
- Add HTML templates for admin and user interfaces
- Add migration scripts and utility files
- Add Docker configuration
- Add documentation files
- Add .gitignore to exclude database and cache files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 00:29:39 +09:00

212 lines
6.6 KiB
Python

"""
프랜차이즈 관리 라우터
- 프랜차이즈 정보 조회
- 프랜차이즈 수정
- 프랜차이즈 전체 통계
"""
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from sqlalchemy import func
from datetime import datetime, date
from database import get_db
from models import Franchise, Store, User, WaitingList, DailyClosing, Member
from schemas import Franchise as FranchiseSchema, FranchiseUpdate
from auth import get_current_user, require_franchise_admin
router = APIRouter()
@router.get("/", response_model=FranchiseSchema)
async def get_franchise(
current_user: User = Depends(require_franchise_admin),
db: Session = Depends(get_db)
):
"""프랜차이즈 정보 조회
Returns:
Franchise: 프랜차이즈 정보
"""
franchise = db.query(Franchise).filter(
Franchise.id == current_user.franchise_id
).first()
if not franchise:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="프랜차이즈를 찾을 수 없습니다"
)
return franchise
@router.put("/{franchise_id}", response_model=FranchiseSchema)
async def update_franchise(
franchise_id: int,
franchise_update: FranchiseUpdate,
current_user: User = Depends(require_franchise_admin),
db: Session = Depends(get_db)
):
"""프랜차이즈 정보 수정
Args:
franchise_id: 프랜차이즈 ID
franchise_update: 수정할 프랜차이즈 정보
Returns:
Franchise: 수정된 프랜차이즈 정보
"""
# 권한 체크: 자신의 프랜차이즈만 수정 가능
if current_user.franchise_id != franchise_id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="다른 프랜차이즈를 수정할 권한이 없습니다"
)
franchise = db.query(Franchise).filter(Franchise.id == franchise_id).first()
if not franchise:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="프랜차이즈를 찾을 수 없습니다"
)
# 수정
update_data = franchise_update.dict(exclude_unset=True)
for key, value in update_data.items():
setattr(franchise, key, value)
franchise.updated_at = datetime.now()
db.commit()
db.refresh(franchise)
return franchise
@router.get("/stats")
async def get_franchise_stats(
current_user: User = Depends(require_franchise_admin),
db: Session = Depends(get_db)
):
"""프랜차이즈 전체 통계 조회
Returns:
dict: 프랜차이즈 전체 통계 정보
"""
franchise_id = current_user.franchise_id
# 매장 수
total_stores = db.query(func.count(Store.id)).filter(
Store.franchise_id == franchise_id,
Store.is_active == True
).scalar()
# 활성 매장 수
active_stores = db.query(func.count(Store.id)).filter(
Store.franchise_id == franchise_id,
Store.is_active == True
).scalar()
# 오늘 날짜
today = date.today()
# 프랜차이즈 전체 매장의 오늘 통계 Query
query = db.query(Store).filter(
Store.franchise_id == franchise_id,
Store.is_active == True
)
# franchise_manager인 경우 관리 매장만 필터링
if current_user.role == 'franchise_manager':
managed_ids = [s.id for s in current_user.managed_stores]
if not managed_ids:
# 관리 매장이 없는 경우 빈 결과 반환
return {
'franchise_id': franchise_id,
'total_stores': 0,
'active_stores': 0,
'total_users': 0, # Note: This might need adjustment if users are also scoped
'total_members': 0,
'today_stats': {
'total_waiting': 0,
'total_attended': 0,
'total_cancelled': 0
},
'current_waiting': 0,
'stores': []
}
query = query.filter(Store.id.in_(managed_ids))
stores = query.all()
store_ids = [store.id for store in stores]
# 오늘의 대기 통계 (모든 매장 합계)
today_stats = db.query(
func.coalesce(func.sum(DailyClosing.total_waiting), 0).label('total_waiting'),
func.coalesce(func.sum(DailyClosing.total_attended), 0).label('total_attended'),
func.coalesce(func.sum(DailyClosing.total_cancelled), 0).label('total_cancelled')
).filter(
DailyClosing.store_id.in_(store_ids),
DailyClosing.business_date == today
).first()
# 현재 대기 중인 고객 수 (모든 매장 합계)
current_waiting = db.query(func.count(WaitingList.id)).filter(
WaitingList.store_id.in_(store_ids),
WaitingList.status == 'waiting'
).scalar()
# 총 사용자 수
total_users = db.query(func.count(User.id)).filter(
User.franchise_id == franchise_id
).scalar()
# 총 회원 수 (모든 매장 합계)
total_members = db.query(func.count(Member.id)).filter(
Member.store_id.in_(store_ids)
).scalar() if store_ids else 0
# 매장별 간단한 통계
store_stats = []
for store in stores:
# 매장의 오늘 통계
store_today = db.query(DailyClosing).filter(
DailyClosing.store_id == store.id,
DailyClosing.business_date == today
).first()
# 매장의 현재 대기 수
store_waiting = db.query(func.count(WaitingList.id)).filter(
WaitingList.store_id == store.id,
WaitingList.status == 'waiting'
).scalar()
store_stats.append({
'store_id': store.id,
'store_name': store.name,
'store_code': store.code,
'current_waiting': store_waiting,
'today_total': store_today.total_waiting if store_today else 0,
'today_attended': store_today.total_attended if store_today else 0,
'today_cancelled': store_today.total_cancelled if store_today else 0,
'is_open': store_today.is_closed == False if store_today else False
})
return {
'franchise_id': franchise_id,
'total_stores': total_stores,
'active_stores': active_stores,
'total_users': total_users,
'total_members': total_members,
'today_stats': {
'total_waiting': today_stats.total_waiting if today_stats else 0,
'total_attended': today_stats.total_attended if today_stats else 0,
'total_cancelled': today_stats.total_cancelled if today_stats else 0
},
'current_waiting': current_waiting,
'stores': store_stats
}