|
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form |
|
from fastapi.responses import StreamingResponse |
|
from sqlalchemy.orm import Session |
|
from typing import List, Optional |
|
import os |
|
import shutil |
|
from datetime import datetime, timezone |
|
from ..utils.pdf_generator import generate_bill_pdf, generate_multi_order_bill_pdf |
|
from pydantic import BaseModel |
|
|
|
from ..database import get_db, Order, Dish, OrderItem, Person, Settings |
|
from ..models.order import Order as OrderModel |
|
from ..models.dish import Dish as DishModel, DishCreate, DishUpdate |
|
|
|
router = APIRouter( |
|
prefix="/admin", |
|
tags=["admin"], |
|
responses={404: {"description": "Not found"}}, |
|
) |
|
|
|
|
|
|
|
@router.get("/orders", response_model=List[OrderModel]) |
|
def get_all_orders(status: str = None, db: Session = Depends(get_db)): |
|
query = db.query(Order) |
|
|
|
if status: |
|
query = query.filter(Order.status == status) |
|
|
|
|
|
orders = query.order_by(Order.created_at.desc()).all() |
|
|
|
|
|
for order in orders: |
|
if order.person_id: |
|
person = db.query(Person).filter(Person.id == order.person_id).first() |
|
if person: |
|
|
|
order.person_name = person.username |
|
order.visit_count = person.visit_count |
|
|
|
|
|
for item in order.items: |
|
if not hasattr(item, "dish") or item.dish is None: |
|
dish = db.query(Dish).filter(Dish.id == item.dish_id).first() |
|
if dish: |
|
item.dish = dish |
|
|
|
return orders |
|
|
|
|
|
|
|
@router.get("/api/dishes", response_model=List[DishModel]) |
|
def get_all_dishes( |
|
is_offer: Optional[int] = None, |
|
is_special: Optional[int] = None, |
|
db: Session = Depends(get_db), |
|
): |
|
query = db.query(Dish) |
|
|
|
if is_offer is not None: |
|
query = query.filter(Dish.is_offer == is_offer) |
|
|
|
if is_special is not None: |
|
query = query.filter(Dish.is_special == is_special) |
|
|
|
dishes = query.all() |
|
return dishes |
|
|
|
|
|
|
|
@router.get("/api/offers", response_model=List[DishModel]) |
|
def get_offer_dishes(db: Session = Depends(get_db)): |
|
dishes = db.query(Dish).filter(Dish.is_offer == 1).all() |
|
return dishes |
|
|
|
|
|
|
|
@router.get("/api/specials", response_model=List[DishModel]) |
|
def get_special_dishes(db: Session = Depends(get_db)): |
|
dishes = db.query(Dish).filter(Dish.is_special == 1).all() |
|
return dishes |
|
|
|
|
|
|
|
@router.get("/api/dishes/{dish_id}", response_model=DishModel) |
|
def get_dish(dish_id: int, db: Session = Depends(get_db)): |
|
dish = db.query(Dish).filter(Dish.id == dish_id).first() |
|
if dish is None: |
|
raise HTTPException(status_code=404, detail="Dish not found") |
|
return dish |
|
|
|
|
|
|
|
@router.get("/api/categories") |
|
def get_all_categories(db: Session = Depends(get_db)): |
|
categories = db.query(Dish.category).distinct().all() |
|
return [category[0] for category in categories] |
|
|
|
|
|
|
|
@router.post("/api/categories") |
|
def create_category(category_name: str = Form(...), db: Session = Depends(get_db)): |
|
|
|
existing_category = ( |
|
db.query(Dish.category).filter(Dish.category == category_name).first() |
|
) |
|
if existing_category: |
|
raise HTTPException(status_code=400, detail="Category already exists") |
|
|
|
return {"message": "Category created successfully", "category": category_name} |
|
|
|
|
|
|
|
@router.post("/api/dishes", response_model=DishModel) |
|
async def create_dish( |
|
name: str = Form(...), |
|
description: Optional[str] = Form(None), |
|
category: str = Form(...), |
|
new_category: Optional[str] = Form(None), |
|
price: float = Form(...), |
|
quantity: int = Form(...), |
|
discount: Optional[float] = Form(0), |
|
is_offer: Optional[int] = Form(0), |
|
is_special: Optional[int] = Form(0), |
|
image: Optional[UploadFile] = File(None), |
|
db: Session = Depends(get_db), |
|
): |
|
|
|
final_category = new_category if new_category else category |
|
|
|
|
|
db_dish = Dish( |
|
name=name, |
|
description=description, |
|
category=final_category, |
|
price=price, |
|
quantity=quantity, |
|
discount=discount, |
|
is_offer=is_offer, |
|
is_special=is_special, |
|
) |
|
|
|
|
|
db.add(db_dish) |
|
db.commit() |
|
db.refresh(db_dish) |
|
|
|
|
|
if image: |
|
|
|
os.makedirs("app/static/images/dishes", exist_ok=True) |
|
|
|
|
|
image_path = f"app/static/images/dishes/{db_dish.id}_{image.filename}" |
|
with open(image_path, "wb") as buffer: |
|
shutil.copyfileobj(image.file, buffer) |
|
|
|
|
|
db_dish.image_path = f"/static/images/dishes/{db_dish.id}_{image.filename}" |
|
db.commit() |
|
db.refresh(db_dish) |
|
|
|
return db_dish |
|
|
|
|
|
|
|
@router.put("/api/dishes/{dish_id}", response_model=DishModel) |
|
async def update_dish( |
|
dish_id: int, |
|
name: Optional[str] = Form(None), |
|
description: Optional[str] = Form(None), |
|
category: Optional[str] = Form(None), |
|
new_category: Optional[str] = Form(None), |
|
price: Optional[float] = Form(None), |
|
quantity: Optional[int] = Form(None), |
|
discount: Optional[float] = Form(None), |
|
is_offer: Optional[int] = Form(None), |
|
is_special: Optional[int] = Form(None), |
|
image: Optional[UploadFile] = File(None), |
|
db: Session = Depends(get_db), |
|
): |
|
|
|
db_dish = db.query(Dish).filter(Dish.id == dish_id).first() |
|
if db_dish is None: |
|
raise HTTPException(status_code=404, detail="Dish not found") |
|
|
|
|
|
if name: |
|
db_dish.name = name |
|
if description: |
|
db_dish.description = description |
|
if new_category: |
|
db_dish.category = new_category |
|
elif category: |
|
db_dish.category = category |
|
if price: |
|
db_dish.price = price |
|
if quantity: |
|
db_dish.quantity = quantity |
|
if discount is not None: |
|
db_dish.discount = discount |
|
if is_offer is not None: |
|
db_dish.is_offer = is_offer |
|
if is_special is not None: |
|
db_dish.is_special = is_special |
|
|
|
|
|
if image: |
|
|
|
os.makedirs("app/static/images/dishes", exist_ok=True) |
|
|
|
|
|
image_path = f"app/static/images/dishes/{db_dish.id}_{image.filename}" |
|
with open(image_path, "wb") as buffer: |
|
shutil.copyfileobj(image.file, buffer) |
|
|
|
|
|
db_dish.image_path = f"/static/images/dishes/{db_dish.id}_{image.filename}" |
|
|
|
|
|
db_dish.updated_at = datetime.now(timezone.utc) |
|
|
|
|
|
db.commit() |
|
db.refresh(db_dish) |
|
|
|
return db_dish |
|
|
|
|
|
|
|
@router.delete("/api/dishes/{dish_id}") |
|
def delete_dish(dish_id: int, db: Session = Depends(get_db)): |
|
db_dish = db.query(Dish).filter(Dish.id == dish_id).first() |
|
if db_dish is None: |
|
raise HTTPException(status_code=404, detail="Dish not found") |
|
|
|
db.delete(db_dish) |
|
db.commit() |
|
|
|
return {"message": "Dish deleted successfully"} |
|
|
|
|
|
|
|
@router.get("/stats/orders") |
|
def get_order_stats(db: Session = Depends(get_db)): |
|
total_orders = db.query(Order).count() |
|
pending_orders = db.query(Order).filter(Order.status == "pending").count() |
|
completed_orders = db.query(Order).filter(Order.status == "completed").count() |
|
payment_requested = ( |
|
db.query(Order).filter(Order.status == "payment_requested").count() |
|
) |
|
paid_orders = db.query(Order).filter(Order.status == "paid").count() |
|
|
|
return { |
|
"total_orders": total_orders, |
|
"pending_orders": pending_orders, |
|
"completed_orders": completed_orders, |
|
"payment_requested": payment_requested, |
|
"paid_orders": paid_orders, |
|
} |
|
|
|
|
|
|
|
@router.put("/orders/{order_id}/paid") |
|
def mark_order_paid(order_id: int, db: Session = Depends(get_db)): |
|
db_order = db.query(Order).filter(Order.id == order_id).first() |
|
if db_order is None: |
|
raise HTTPException(status_code=404, detail="Order not found") |
|
|
|
|
|
db_order.status = "paid" |
|
db_order.updated_at = datetime.now(timezone.utc) |
|
|
|
db.commit() |
|
|
|
return {"message": "Order marked as paid"} |
|
|
|
|
|
|
|
@router.get("/orders/{order_id}/bill") |
|
def generate_bill(order_id: int, db: Session = Depends(get_db)): |
|
|
|
db_order = db.query(Order).filter(Order.id == order_id).first() |
|
if db_order is None: |
|
raise HTTPException(status_code=404, detail="Order not found") |
|
|
|
|
|
if db_order.person_id: |
|
person = db.query(Person).filter(Person.id == db_order.person_id).first() |
|
if person: |
|
db_order.person_name = person.username |
|
|
|
|
|
for item in db_order.items: |
|
if not hasattr(item, "dish") or item.dish is None: |
|
dish = db.query(Dish).filter(Dish.id == item.dish_id).first() |
|
if dish: |
|
item.dish = dish |
|
|
|
|
|
settings = db.query(Settings).first() |
|
if not settings: |
|
|
|
settings = Settings( |
|
hotel_name="Tabble Hotel", |
|
address="123 Main Street, City", |
|
contact_number="+1 123-456-7890", |
|
email="[email protected]", |
|
) |
|
db.add(settings) |
|
db.commit() |
|
db.refresh(settings) |
|
|
|
|
|
pdf_buffer = generate_bill_pdf(db_order, settings) |
|
|
|
|
|
filename = f"bill_order_{order_id}_{datetime.now().strftime('%Y%m%d%H%M%S')}.pdf" |
|
|
|
return StreamingResponse( |
|
pdf_buffer, |
|
media_type="application/pdf", |
|
headers={"Content-Disposition": f"attachment; filename={filename}"} |
|
) |
|
|
|
|
|
|
|
@router.post("/orders/multi-bill") |
|
def generate_multi_bill(order_ids: List[int], db: Session = Depends(get_db)): |
|
if not order_ids: |
|
raise HTTPException(status_code=400, detail="No order IDs provided") |
|
|
|
orders = [] |
|
|
|
|
|
for order_id in order_ids: |
|
db_order = db.query(Order).filter(Order.id == order_id).first() |
|
if db_order is None: |
|
raise HTTPException(status_code=404, detail=f"Order {order_id} not found") |
|
|
|
|
|
if db_order.person_id: |
|
person = db.query(Person).filter(Person.id == db_order.person_id).first() |
|
if person: |
|
db_order.person_name = person.username |
|
|
|
|
|
for item in db_order.items: |
|
if not hasattr(item, "dish") or item.dish is None: |
|
dish = db.query(Dish).filter(Dish.id == item.dish_id).first() |
|
if dish: |
|
item.dish = dish |
|
|
|
orders.append(db_order) |
|
|
|
|
|
settings = db.query(Settings).first() |
|
if not settings: |
|
|
|
settings = Settings( |
|
hotel_name="Tabble Hotel", |
|
address="123 Main Street, City", |
|
contact_number="+1 123-456-7890", |
|
email="[email protected]", |
|
) |
|
db.add(settings) |
|
db.commit() |
|
db.refresh(settings) |
|
|
|
|
|
pdf_buffer = generate_multi_order_bill_pdf(orders, settings) |
|
|
|
|
|
order_ids_str = "-".join([str(order_id) for order_id in order_ids]) |
|
filename = f"bill_orders_{order_ids_str}_{datetime.now().strftime('%Y%m%d%H%M%S')}.pdf" |
|
|
|
return StreamingResponse( |
|
pdf_buffer, |
|
media_type="application/pdf", |
|
headers={"Content-Disposition": f"attachment; filename={filename}"} |
|
) |
|
|
|
|
|
|
|
@router.post("/orders/merge") |
|
def merge_orders(source_order_id: int, target_order_id: int, db: Session = Depends(get_db)): |
|
|
|
source_order = db.query(Order).filter(Order.id == source_order_id).first() |
|
target_order = db.query(Order).filter(Order.id == target_order_id).first() |
|
|
|
if not source_order: |
|
raise HTTPException(status_code=404, detail=f"Source order {source_order_id} not found") |
|
|
|
if not target_order: |
|
raise HTTPException(status_code=404, detail=f"Target order {target_order_id} not found") |
|
|
|
|
|
valid_statuses = ["completed", "paid"] |
|
if source_order.status not in valid_statuses: |
|
raise HTTPException(status_code=400, detail=f"Source order must be completed or paid, current status: {source_order.status}") |
|
|
|
if target_order.status not in valid_statuses: |
|
raise HTTPException(status_code=400, detail=f"Target order must be completed or paid, current status: {target_order.status}") |
|
|
|
|
|
for item in source_order.items: |
|
|
|
item.order_id = target_order.id |
|
|
|
|
|
target_order.updated_at = datetime.now(timezone.utc) |
|
|
|
|
|
db.delete(source_order) |
|
|
|
|
|
db.commit() |
|
|
|
|
|
db.refresh(target_order) |
|
|
|
return {"message": f"Orders merged successfully. Items from order #{source_order_id} have been moved to order #{target_order_id}"} |
|
|