Files
MyPOS/app/tenant/waitlist/routes.py
T
2026-05-07 12:17:19 -04:00

132 lines
4.9 KiB
Python

"""
app/tenant/waitlist/routes.py
Waitlist management: list, add, notify, mark booked, expire.
"""
import logging
from datetime import datetime, timezone
from flask import Blueprint, render_template, redirect, url_for, flash, request, g
from flask_login import login_required
from app.extensions import db, mail
from app.models.salon import Waitlist, Staff, Service
from app.decorators import require_role, demo_readonly, tenant_feature_required
from app.tenant.utils import log_tenant_action
logger = logging.getLogger(__name__)
waitlist_bp = Blueprint("waitlist", __name__, url_prefix="/waitlist")
@waitlist_bp.route("/")
@login_required
@require_role("tenant_admin", "tenant_manager")
@tenant_feature_required("waitlist")
def index():
entries = Waitlist.query.filter_by(
tenant_id=g.tenant.id, location_id=g.location.id
).filter(
Waitlist.status.in_(["waiting", "notified"])
).order_by(Waitlist.created_at).all()
return render_template("tenant/waitlist/index.html", entries=entries)
@waitlist_bp.route("/add", methods=["GET", "POST"])
@login_required
@require_role("tenant_admin", "tenant_manager")
@demo_readonly
@tenant_feature_required("waitlist")
def add():
staff_list = Staff.query.filter_by(
tenant_id=g.tenant.id, is_active=True).filter(
Staff.deleted_at.is_(None)).order_by(Staff.name).all()
services = Service.query.filter_by(
tenant_id=g.tenant.id, is_active=True).filter(
Service.deleted_at.is_(None)).order_by(Service.name).all()
if request.method == "POST":
name = request.form.get("customer_name", "").strip()
phone = request.form.get("customer_phone", "").strip() or None
email_addr = request.form.get("customer_email", "").strip() or None
if not name:
return render_template("tenant/waitlist/form.html",
staff_list=staff_list, services=services,
error="Customer name is required.")
entry = Waitlist(
tenant_id=g.tenant.id, location_id=g.location.id,
customer_name=name, customer_phone=phone,
customer_email=email_addr,
staff_id=request.form.get("staff_id", type=int) or None,
service_id=request.form.get("service_id", type=int) or None,
requested_date=_parse_date(request.form.get("requested_date", "")),
status="waiting",
)
db.session.add(entry)
db.session.flush()
log_tenant_action("waitlist.add", "waitlist", entry.id, {"name": name})
db.session.commit()
flash(f"'{name}' added to waitlist.", "success")
return redirect(url_for("waitlist.index"))
return render_template("tenant/waitlist/form.html",
staff_list=staff_list, services=services)
@waitlist_bp.route("/<int:entry_id>/notify", methods=["POST"])
@login_required
@require_role("tenant_admin", "tenant_manager")
@demo_readonly
@tenant_feature_required("waitlist")
def notify(entry_id):
entry = Waitlist.query.filter_by(
id=entry_id, tenant_id=g.tenant.id).first_or_404()
entry.status = "notified"
entry.notified_at = datetime.now(timezone.utc)
# Send email notification if available
if entry.customer_email:
try:
from flask_mail import Message
msg = Message(
subject=f"A slot is available — {g.tenant.name}",
recipients=[entry.customer_email],
body=(
f"Hi {entry.customer_name},\n\n"
"A slot has opened up for you. Please call or book online to confirm.\n\n"
f"{g.tenant.name}"
),
)
mail.send(msg)
except Exception as exc:
logger.error("Waitlist notify email failed: %s", exc)
log_tenant_action("waitlist.notify", "waitlist", entry.id,
{"name": entry.customer_name})
db.session.commit()
flash(f"'{entry.customer_name}' notified.", "success")
return redirect(url_for("waitlist.index"))
@waitlist_bp.route("/<int:entry_id>/set-status", methods=["POST"])
@login_required
@require_role("tenant_admin", "tenant_manager")
@demo_readonly
@tenant_feature_required("waitlist")
def set_status(entry_id):
entry = Waitlist.query.filter_by(
id=entry_id, tenant_id=g.tenant.id).first_or_404()
new_status = request.form.get("status", "")
if new_status in ("booked", "expired"):
entry.status = new_status
log_tenant_action("waitlist.set_status", "waitlist", entry.id,
{"status": new_status})
db.session.commit()
flash(f"Entry updated to '{new_status}'.", "success")
return redirect(url_for("waitlist.index"))
def _parse_date(value):
if not value:
return None
try:
from datetime import date
return date.fromisoformat(value)
except ValueError:
return None