add order management: status tracking, notes, admin endpoints
This commit is contained in:
@@ -11,6 +11,7 @@ from fastapi.staticfiles import StaticFiles
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
DB_PATH = os.environ.get("DB_PATH", "/srv/form-backend/submissions.db")
|
DB_PATH = os.environ.get("DB_PATH", "/srv/form-backend/submissions.db")
|
||||||
|
STATUSES = ["received", "confirmed", "hardware ready", "installing", "completed", "cancelled"]
|
||||||
|
|
||||||
# ---- DB Helpers ----
|
# ---- DB Helpers ----
|
||||||
|
|
||||||
@@ -67,6 +68,7 @@ def init_schema(db):
|
|||||||
CREATE TABLE IF NOT EXISTS submissions (
|
CREATE TABLE IF NOT EXISTS submissions (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
form_id INTEGER NOT NULL DEFAULT 1 REFERENCES forms(id),
|
form_id INTEGER NOT NULL DEFAULT 1 REFERENCES forms(id),
|
||||||
|
status TEXT NOT NULL DEFAULT 'received',
|
||||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -94,6 +96,13 @@ def init_schema(db):
|
|||||||
token TEXT PRIMARY KEY,
|
token TEXT PRIMARY KEY,
|
||||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS submission_notes (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
submission_id INTEGER NOT NULL REFERENCES submissions(id) ON DELETE CASCADE,
|
||||||
|
note TEXT NOT NULL,
|
||||||
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||||
|
);
|
||||||
""")
|
""")
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
@@ -326,7 +335,7 @@ def check_status(email: str = "", phone: str = ""):
|
|||||||
raise HTTPException(400, "Email and phone are required")
|
raise HTTPException(400, "Email and phone are required")
|
||||||
db = get_db()
|
db = get_db()
|
||||||
rows = db.execute("""
|
rows = db.execute("""
|
||||||
SELECT s.id, s.created_at
|
SELECT s.id, s.status, s.created_at
|
||||||
FROM submissions s
|
FROM submissions s
|
||||||
JOIN submission_data d1 ON d1.submission_id = s.id AND d1.field_label = 'Email' AND d1.field_value = ?
|
JOIN submission_data d1 ON d1.submission_id = s.id AND d1.field_label = 'Email' AND d1.field_value = ?
|
||||||
JOIN submission_data d2 ON d2.submission_id = s.id AND d2.field_label = 'Phone' AND d2.field_value = ?
|
JOIN submission_data d2 ON d2.submission_id = s.id AND d2.field_label = 'Phone' AND d2.field_value = ?
|
||||||
@@ -336,7 +345,8 @@ def check_status(email: str = "", phone: str = ""):
|
|||||||
if not rows:
|
if not rows:
|
||||||
raise HTTPException(404, "No submission found with that email and phone")
|
raise HTTPException(404, "No submission found with that email and phone")
|
||||||
r = rows[0]
|
r = rows[0]
|
||||||
return {"id": r["id"], "created_at": r["created_at"], "status": "received"}
|
status_idx = STATUSES.index(r["status"]) if r["status"] in STATUSES else 0
|
||||||
|
return {"id": r["id"], "created_at": r["created_at"], "status": r["status"], "status_index": status_idx, "total_statuses": len(STATUSES)}
|
||||||
|
|
||||||
# ---- Delete Submission (verify with email + phone) ----
|
# ---- Delete Submission (verify with email + phone) ----
|
||||||
|
|
||||||
@@ -364,6 +374,50 @@ def delete_submission(email: str = "", phone: str = ""):
|
|||||||
db.close()
|
db.close()
|
||||||
return {"ok": True, "deleted_id": sub_id}
|
return {"ok": True, "deleted_id": sub_id}
|
||||||
|
|
||||||
|
# ---- Admin: Update Status ----
|
||||||
|
|
||||||
|
class StatusUpdate(BaseModel):
|
||||||
|
submission_id: int
|
||||||
|
status: str
|
||||||
|
|
||||||
|
@app.post("/admin/status", status_code=200)
|
||||||
|
def update_status(body: StatusUpdate, request: Request):
|
||||||
|
check_auth(request)
|
||||||
|
if body.status not in STATUSES:
|
||||||
|
raise HTTPException(400, f"Invalid status. Must be one of: {', '.join(STATUSES)}")
|
||||||
|
db = get_db()
|
||||||
|
db.execute("UPDATE submissions SET status = ? WHERE id = ?", (body.status, body.submission_id))
|
||||||
|
db.commit()
|
||||||
|
db.close()
|
||||||
|
return {"ok": True}
|
||||||
|
|
||||||
|
# ---- Admin: Add Note ----
|
||||||
|
|
||||||
|
class NoteBody(BaseModel):
|
||||||
|
submission_id: int
|
||||||
|
note: str
|
||||||
|
|
||||||
|
@app.post("/admin/notes", status_code=201)
|
||||||
|
def add_note(body: NoteBody, request: Request):
|
||||||
|
check_auth(request)
|
||||||
|
if not body.note.strip():
|
||||||
|
raise HTTPException(400, "Note cannot be empty")
|
||||||
|
db = get_db()
|
||||||
|
db.execute("INSERT INTO submission_notes (submission_id, note) VALUES (?, ?)", (body.submission_id, body.note.strip()))
|
||||||
|
db.commit()
|
||||||
|
db.close()
|
||||||
|
return {"ok": True}
|
||||||
|
|
||||||
|
# ---- Admin: Get Notes ----
|
||||||
|
|
||||||
|
@app.get("/admin/notes/{submission_id}")
|
||||||
|
def get_notes(submission_id: int, request: Request):
|
||||||
|
check_auth(request)
|
||||||
|
db = get_db()
|
||||||
|
rows = db.execute("SELECT id, note, created_at FROM submission_notes WHERE submission_id = ? ORDER BY id DESC", (submission_id,)).fetchall()
|
||||||
|
db.close()
|
||||||
|
return [dict(r) for r in rows]
|
||||||
|
|
||||||
# ---- Admin Submissions ----
|
# ---- Admin Submissions ----
|
||||||
|
|
||||||
@app.get("/admin/submissions")
|
@app.get("/admin/submissions")
|
||||||
@@ -399,7 +453,7 @@ def admin_submissions(
|
|||||||
).fetchone()
|
).fetchone()
|
||||||
result.append({
|
result.append({
|
||||||
"id": r["id"], "form_id": r["form_id"], "form_name": r["form_name"],
|
"id": r["id"], "form_id": r["form_id"], "form_name": r["form_name"],
|
||||||
"created_at": r["created_at"],
|
"created_at": r["created_at"], "status": r["status"],
|
||||||
"data": [dict(d) for d in data],
|
"data": [dict(d) for d in data],
|
||||||
"tags": [t["tag"] for t in tags],
|
"tags": [t["tag"] for t in tags],
|
||||||
"reply": dict(reply) if reply else None
|
"reply": dict(reply) if reply else None
|
||||||
|
|||||||
Reference in New Issue
Block a user