From bf361d76bc3b84b9ab99f8fc1e23275b81b8302a Mon Sep 17 00:00:00 2001 From: root Date: Sun, 2 Aug 2026 14:37:37 +0000 Subject: [PATCH] no-mistakes(document): Docs synced for Cancelled status; lint cleaned --- AGENTS.md | 5 +++-- PRD.md | 6 ++++-- app/core/security.py | 1 - app/schemas/auth.py | 2 +- app/services/auth.py | 2 -- tests/test_workflow_hardening.py | 5 ++++- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 4e247d7..a3cdd37 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -74,8 +74,9 @@ Frontend: Alpine.js (CDN) + Tailwind CSS (CDN). Auth state in localStorage. Role |--------|------|------|-------------| | POST | `/api/tickets` | Bearer | Create ticket (auto-number PAV-YYYY-NNNNN) | | GET | `/api/tickets` | No | List tickets (filter: status, priority, property, building, unit_id, category_id, assigned_to, date_from, date_to) | -| GET | `/api/tickets/{id}` | No | Get ticket detail with timeline, photos, SLA status | -| PATCH | `/api/tickets/{id}` | Bearer | Update ticket (validates status transitions) | +| GET | `/api/tickets/{id}` | No | Get ticket detail with timeline, photos, SLA status, nested unit/category, phone | +| GET | `/api/tickets/{id}/transitions` | No | Valid next statuses for the ticket's current status (drives the detail-page status picker) | +| PATCH | `/api/tickets/{id}` | Bearer | Update ticket (validates status transitions; assigning a technician auto-advances New/Logged/Triage to Assigned) | | DELETE | `/api/tickets/{id}` | Admin/Jerome, Admin/Wahab | Delete ticket + children (timeline/photos/escalations); test/scratch cleanup only | | POST | `/api/tickets/{id}/status` | Bearer | Change status with note | | GET | `/api/tickets/{id}/sla` | No | Check SLA breach status | diff --git a/PRD.md b/PRD.md index 132ffa0..fff1fe0 100644 --- a/PRD.md +++ b/PRD.md @@ -120,7 +120,7 @@ Close Ticket Archive → Executive Reporting ``` -### Detailed Ticket Lifecycle (15 Statuses) +### Detailed Ticket Lifecycle (16 Statuses) | # | Status | Description | Who Sets | |---|--------|-------------|----------| @@ -139,6 +139,7 @@ Archive → Executive Reporting | 13 | **Wahab Review** | Final QA oversight and closure approval | Wahab | | 14 | **Closed** | Ticket officially closed | Wahab / Nicholas | | 15 | **Reopened** | Issue resurfaces within 7 days | Agent / System | +| 16 | **Cancelled** | Ticket withdrawn (test/scratch cleanup, guest no-show); terminal — reachable from any active state, excluded from SLA breach reporting and dashboard active counts | Any staff (via dashboard) | ### Step-by-Step Detail @@ -154,9 +155,10 @@ Archive → Executive Reporting 9. **Wahab Review** — Wahab does final QA oversight in dashboard → status → Wahab Review → approves or sends back 10. **Auto Follow-up** (48 hours later — internal only) — System flags ticket for Wahab/Nicholas: `Issue PAV-001 (505E WC) — resolved 48h ago. Any follow-up needed?` → If issue resurfaces → auto-reopens, escalates to Ama -### Reopening & Reassignment +### Reopening, Reassignment & Cancellation - **Reopening:** If a verified-closed issue resurfaces within 7 days, the gatekeeper can Reopen it. A reopened issue auto-escalates to Ama. +- **Cancellation:** Any ticket can be cancelled from an active state (terminal; cannot resume work). Cancelled tickets are excluded from SLA breach reporting and dashboard active counts. - **Reassignment:** A technician who cannot complete a job can request: `#reassign PAV-001 [technician name]` → Nicholas receives notification and approves/reassigns in dashboard. --- diff --git a/app/core/security.py b/app/core/security.py index c93eff6..6e0657d 100644 --- a/app/core/security.py +++ b/app/core/security.py @@ -3,7 +3,6 @@ from __future__ import annotations from datetime import datetime, timedelta, timezone -from functools import wraps from typing import Annotated, Any, Callable import bcrypt diff --git a/app/schemas/auth.py b/app/schemas/auth.py index cf2b704..3febf54 100644 --- a/app/schemas/auth.py +++ b/app/schemas/auth.py @@ -2,7 +2,7 @@ from __future__ import annotations -from pydantic import BaseModel, EmailStr +from pydantic import BaseModel class RegisterRequest(BaseModel): diff --git a/app/services/auth.py b/app/services/auth.py index de505a7..9bed864 100644 --- a/app/services/auth.py +++ b/app/services/auth.py @@ -3,11 +3,9 @@ from __future__ import annotations from fastapi import HTTPException, status -from jose import JWTError, jwt from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession -from app.core.config import settings from app.core.security import ( create_access_token, create_refresh_token, diff --git a/tests/test_workflow_hardening.py b/tests/test_workflow_hardening.py index fad9a85..8a157a0 100644 --- a/tests/test_workflow_hardening.py +++ b/tests/test_workflow_hardening.py @@ -281,7 +281,10 @@ async def test_ticket_number_uses_max_plus_one(client, seed_tickets): await session.commit() fourth = await _create_ticket(client, token, description="numbering d") - suffix = lambda tn: int(tn.rsplit("-", 1)[1]) + + def suffix(tn: str) -> int: + return int(tn.rsplit("-", 1)[1]) + survivors = {suffix(first["ticket_number"]), suffix(third["ticket_number"])} assert suffix(fourth["ticket_number"]) not in survivors assert suffix(fourth["ticket_number"]) > max(survivors)