147 lines
5.7 KiB
Python
147 lines
5.7 KiB
Python
from django.shortcuts import render, get_object_or_404, redirect
|
|
from django.http import JsonResponse
|
|
from django.db import transaction
|
|
from django.db.models import Sum, F
|
|
from django.utils import timezone
|
|
from django.contrib import messages
|
|
from django.contrib.auth import login, logout, authenticate
|
|
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
|
|
from django.contrib.auth.decorators import login_required, user_passes_test
|
|
from django.contrib.auth.models import User
|
|
from .models import Ingredient, MenuItem, MenuItemIngredient, Order, OrderItem, UserProfile
|
|
import json
|
|
|
|
def is_manager(user):
|
|
return user.is_authenticated and hasattr(user, 'profile') and user.profile.role == 'manager'
|
|
|
|
def is_cashier(user):
|
|
return user.is_authenticated and hasattr(user, 'profile') and user.profile.role == 'cashier'
|
|
|
|
def manager_required(view_func):
|
|
return user_passes_test(is_manager, login_url='login')(view_func)
|
|
|
|
def cashier_or_manager_required(view_func):
|
|
return user_passes_test(lambda u: is_manager(u) or is_cashier(u), login_url='login')(view_func)
|
|
|
|
def login_view(request):
|
|
if request.method == 'POST':
|
|
form = AuthenticationForm(request, data=request.POST)
|
|
if form.is_valid():
|
|
user = form.get_user()
|
|
login(request, user)
|
|
if is_manager(user):
|
|
return redirect('dashboard')
|
|
return redirect('pos')
|
|
else:
|
|
form = AuthenticationForm()
|
|
return render(request, 'core/login.html', {'form': form})
|
|
|
|
def logout_view(request):
|
|
logout(request)
|
|
return redirect('login')
|
|
|
|
def home(request):
|
|
"""Redirect home to login or dashboard/pos based on role."""
|
|
if request.user.is_authenticated:
|
|
if is_manager(request.user):
|
|
return redirect('dashboard')
|
|
return redirect('pos')
|
|
return render(request, "core/index.html")
|
|
|
|
@cashier_or_manager_required
|
|
def pos_view(request):
|
|
"""Cashier POS interface."""
|
|
menu_items = MenuItem.objects.filter(is_active=True)
|
|
return render(request, "core/pos.html", {"menu_items": menu_items})
|
|
|
|
@cashier_or_manager_required
|
|
def create_order(request):
|
|
"""Handle order creation and stock deduction."""
|
|
if request.method == "POST":
|
|
try:
|
|
data = json.loads(request.body)
|
|
cart = data.get("cart", [])
|
|
notes = data.get("notes", "")
|
|
|
|
if not cart:
|
|
return JsonResponse({"success": False, "error": "Cart is empty"}, status=400)
|
|
|
|
with transaction.atomic():
|
|
order = Order.objects.create(customer_notes=notes)
|
|
total_price = 0
|
|
|
|
for item in cart:
|
|
menu_item = get_object_or_404(MenuItem, id=item["id"])
|
|
quantity = int(item["quantity"])
|
|
|
|
# Deduct stock
|
|
for recipe_item in menu_item.ingredients.all():
|
|
required_qty = recipe_item.quantity_required * quantity
|
|
ingredient = recipe_item.ingredient
|
|
if ingredient.stock_quantity < required_qty:
|
|
raise Exception(f"Insufficient stock for {ingredient.name}")
|
|
|
|
ingredient.stock_quantity = F('stock_quantity') - required_qty
|
|
ingredient.save()
|
|
|
|
# Create OrderItem
|
|
OrderItem.objects.create(
|
|
order=order,
|
|
menu_item=menu_item,
|
|
quantity=quantity,
|
|
price_at_order=menu_item.price
|
|
)
|
|
total_price += menu_item.price * quantity
|
|
|
|
order.total_price = total_price
|
|
order.save()
|
|
|
|
return JsonResponse({"success": True, "order_number": order.order_number})
|
|
except Exception as e:
|
|
return JsonResponse({"success": False, "error": str(e)}, status=400)
|
|
return JsonResponse({"success": False, "error": "Invalid request"}, status=405)
|
|
|
|
@cashier_or_manager_required
|
|
def receipt_view(request, order_number):
|
|
"""Printable receipt view."""
|
|
order = get_object_or_404(Order, order_number=order_number)
|
|
return render(request, "core/receipt.html", {"order": order})
|
|
|
|
@manager_required
|
|
def dashboard_view(request):
|
|
"""Manager dashboard for stock and reports."""
|
|
ingredients = Ingredient.objects.all()
|
|
# Summary of last 30 orders
|
|
orders = Order.objects.prefetch_related('items__menu_item').order_by('-created_at')[:50]
|
|
total_sales = Order.objects.aggregate(Sum('total_price'))['total_price__sum'] or 0
|
|
|
|
return render(request, "core/dashboard.html", {
|
|
"ingredients": ingredients,
|
|
"orders": orders,
|
|
"total_sales": total_sales,
|
|
})
|
|
|
|
@manager_required
|
|
def manage_users(request):
|
|
"""Manager only: Create and view accounts."""
|
|
users = User.objects.all().select_related('profile')
|
|
if request.method == 'POST':
|
|
username = request.POST.get('username')
|
|
password = request.POST.get('password')
|
|
role = request.POST.get('role')
|
|
|
|
if username and password and role:
|
|
if User.objects.filter(username=username).exists():
|
|
messages.error(request, f"User {username} already exists.")
|
|
else:
|
|
user = User.objects.create_user(username=username, password=password)
|
|
profile, created = UserProfile.objects.get_or_create(user=user)
|
|
profile.role = role
|
|
profile.save()
|
|
messages.success(request, f"User {username} created as {role}.")
|
|
return redirect('manage_users')
|
|
else:
|
|
messages.error(request, "Please fill all fields.")
|
|
|
|
return render(request, 'core/user_management.html', {'users': users})
|