@@ -39,7 +39,7 @@ Users, units, and categories are auto-seeded on first startup via lifespan hook:
- 120 apartment units (East/West, 10 floors × 6 apts per wing)
- 120 apartment units (East/West, 10 floors × 6 apts per wing)
- Default password for all seed users: `denya123`
- Default password for all seed users: `denya123`
- Units load from `apartment_mapping.json` if present, else built-in fallback
- Units load from `apartment_mapping.json` if present, else built-in fallback
- Categories: 20 top-level (10 Maintenance, 6 CS, 4 Emergency) with sub-categories, seeded from `app/services/seed.py::SEED_CATEGORIES_DATA`
- Categories: 23 top-level (13 Maintenance, 6 CS, 4 Emergency) with sub-categories, seeded from `app/services/seed.py::SEED_CATEGORIES_DATA`
## Key API endpoints
## Key API endpoints
@@ -72,15 +72,15 @@ Frontend: Alpine.js (CDN) + Tailwind CSS (CDN). Auth state in localStorage. Role
| Method | Path | Auth | Description |
| Method | Path | Auth | Description |
|--------|------|------|-------------|
|--------|------|------|-------------|
| POST | `/api/tickets` | Bearer | Create ticket (auto-number PAV-YYYY-NNNNN) |
| POST | `/api/tickets` | Bearer | Create ticket (auto-number PAV-YYYY-NNNNN) |
| GET | `/api/tickets` | No | List tickets (filter: status, priority, property, category_id, assigned_to, date_from, date_to) |
| 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 |
| GET | `/api/tickets/{id}` | No | Get ticket detail with timeline, photos, SLA status |
| PATCH | `/api/tickets/{id}` | Bearer | Update ticket (validates status transitions) |
| PATCH | `/api/tickets/{id}` | Bearer | Update ticket (validates status transitions) |
| POST | `/api/tickets/{id}/status` | Bearer | Change status with note |
| POST | `/api/tickets/{id}/status` | Bearer | Change status with note |
| GET | `/api/tickets/{id}/sla` | No | Check SLA breach status |
| GET | `/api/tickets/{id}/sla` | No | Check SLA breach status |
| POST | `/api/tickets/{id}/photos` | Bearer | Upload photos (multipart, is_before param) |
| POST | `/api/tickets/{id}/photos` | Bearer | Upload photos (multipart, is_before param) |
| GET | `/api/tickets/{id}/photos` | No | List photos |
| GET | `/api/tickets/{id}/photos` | No | List photos |
| GET | `/api/tickets/categories` | No | Category tree (optional `? type= ` filter ) |
| GET | `/api/tickets/categories` | No | Category tree (filters: `type` ; alert-only hidden unless `include_hidden=true` ) |
| GET | `/api/tickets/categories/flat` | No | Flat category list |
| GET | `/api/tickets/categories/flat` | No | Flat category list (same `type` / `include_hidden` filters) |
## Auth
## Auth
@@ -123,7 +123,9 @@ Tables: users, units, categories, tickets, ticket_timeline, ticket_photos, escal
(`/api/tickets/categories[/flat]` ) exclude them unless `include_hidden=true` (used by the
(`/api/tickets/categories[/flat]` ) exclude them unless `include_hidden=true` (used by the
emergency quick path on `tickets/new.html` ). Seed taxonomy lives in `app/services/seed.py`
emergency quick path on `tickets/new.html` ). Seed taxonomy lives in `app/services/seed.py`
(`SEED_CATEGORIES_DATA` ); the Lost Property → Missing Item rename is a data migration
(`SEED_CATEGORIES_DATA` ); the Lost Property → Missing Item rename is a data migration
(alembic `b2f4a6c8e0d2` ). Seeds are idempotent and sync `show_in_form` on existing rows.
(alembic `b2f4a6c8e0d2` ), with a startup self-heal ( `app/main.py::ensure_legacy_schema` )
applying the same column/rename fix to legacy create_all databases. Seeds are idempotent
and sync `show_in_form` on existing rows.
- Location hierarchy is Property → Building → Apartment (uses `Unit.building` ).
- Location hierarchy is Property → Building → Apartment (uses `Unit.building` ).
`GET /api/tickets/units/grouped` returns `{property: {building: [units]}}` ;
`GET /api/tickets/units/grouped` returns `{property: {building: [units]}}` ;
`/api/tickets` accepts additive `building` /`unit_id` filters. Unit data is deterministic
`/api/tickets` accepts additive `building` /`unit_id` filters. Unit data is deterministic