diff --git a/main.py b/main.py index 6cbe255..26f8607 100644 --- a/main.py +++ b/main.py @@ -4,14 +4,15 @@ Admin configuration: edit the CONFIG dict below. """ import sqlite3 +import json import os from contextlib import contextmanager, asynccontextmanager from datetime import datetime, timezone from pathlib import Path import bcrypt -from fastapi import FastAPI, HTTPException, Request -from fastapi.responses import HTMLResponse, JSONResponse +from fastapi import FastAPI, HTTPException +from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.staticfiles import StaticFiles from pydantic import BaseModel, field_validator from typing import Optional @@ -32,6 +33,8 @@ CONFIG = { } DB_PATH = CONFIG["db_path"] +static_dir = Path(__file__).parent / "static" +STAFF_FILE = Path(__file__).parent / "staff.json" # --------------------------------------------------------------------------- # Database @@ -116,19 +119,25 @@ def format_amount(pence: int) -> str: div = CONFIG["currency_divisor"] return f"{sym}{pence / div:.2f}" +def load_staff() -> list: + if STAFF_FILE.exists(): + return json.loads(STAFF_FILE.read_text()).get("staff", []) + return [] + +def save_staff(names: list): + STAFF_FILE.write_text(json.dumps({"staff": sorted(set(names))}, indent=2)) + # --------------------------------------------------------------------------- # FastAPI app # --------------------------------------------------------------------------- @asynccontextmanager -async def lifespan(app: FastAPI): +async def lifespan(app): init_db() yield app = FastAPI(title=CONFIG["club_name"], lifespan=lifespan) -# Serve static files -static_dir = Path(__file__).parent / "static" static_dir.mkdir(exist_ok=True) app.mount("/static", StaticFiles(directory=str(static_dir)), name="static") @@ -158,13 +167,13 @@ class MemberCreate(BaseModel): class TopupRequest(BaseModel): member_id: int - amount: int # pence + amount: int staff_name: str note: Optional[str] = None class ChargeRequest(BaseModel): member_id: int - amount: int # pence + amount: int pin: str staff_name: str note: Optional[str] = None @@ -176,13 +185,28 @@ class ProductCreate(BaseModel): member_price: Optional[int] = None search_tags: Optional[str] = None +class StaffAdd(BaseModel): + name: str + # --------------------------------------------------------------------------- -# Endpoints +# Page routes # --------------------------------------------------------------------------- -@app.get("/", response_class=HTMLResponse) +@app.get("/", response_class=RedirectResponse) async def root(): - return (static_dir / "index.html").read_text() + return RedirectResponse(url="/cashier", status_code=302) + +@app.get("/cashier", response_class=HTMLResponse) +async def cashier_page(): + return (static_dir / "cashier.html").read_text() + +@app.get("/bar", response_class=HTMLResponse) +async def bar_page(): + return (static_dir / "bar.html").read_text() + +# --------------------------------------------------------------------------- +# API endpoints – members +# --------------------------------------------------------------------------- @app.post("/members") def create_member(body: MemberCreate): @@ -242,13 +266,15 @@ def topup(body: TopupRequest): member = conn.execute("SELECT * FROM members WHERE id=?", (body.member_id,)).fetchone() if not member: raise HTTPException(404, "Member not found") - conn.execute( + cur = conn.execute( "INSERT INTO ledger_entries (member_id, amount, type, venue, note, staff_name) VALUES (?,?,?,?,?,?)", (body.member_id, body.amount, "topup", "cashier", body.note, body.staff_name) ) + entry_id = cur.lastrowid balance = member_balance(conn, body.member_id) return { "ok": True, + "entry_id": entry_id, "new_balance": balance, "new_balance_display": format_amount(balance), } @@ -268,13 +294,15 @@ def charge(body: ChargeRequest): balance = member_balance(conn, body.member_id) if not CONFIG["allow_negative_balance"] and balance < body.amount: raise HTTPException(400, f"Insufficient balance ({format_amount(balance)})") - conn.execute( + cur = conn.execute( "INSERT INTO ledger_entries (member_id, amount, type, venue, note, staff_name) VALUES (?,?,?,?,?,?)", (body.member_id, body.amount, "charge", "bar", body.note, body.staff_name) ) + entry_id = cur.lastrowid new_balance = member_balance(conn, body.member_id) return { "ok": True, + "entry_id": entry_id, "new_balance": new_balance, "new_balance_display": format_amount(new_balance), } @@ -315,6 +343,39 @@ def transactions(member_id: int, limit: int = 50, offset: int = 0): ], } +# --------------------------------------------------------------------------- +# Print views +# --------------------------------------------------------------------------- + +def _print_size_script() -> str: + return """ +""" + +def _print_controls(extra_class: str = "") -> str: + return f"""