no-mistakes(review): Fix F01/F02/F05: technician name, duplicate timeline, FM dashboard property counts

This commit is contained in:
root
2026-07-23 19:13:10 +00:00
parent 7fab1ede51
commit 5560496653
4 changed files with 14 additions and 12 deletions
+4
View File
@@ -70,6 +70,10 @@ class Ticket(Base):
photos = relationship("TicketPhoto", back_populates="ticket") photos = relationship("TicketPhoto", back_populates="ticket")
escalations = relationship("Escalation", back_populates="ticket") escalations = relationship("Escalation", back_populates="ticket")
@property
def assigned_technician_name(self) -> str | None:
return self.assigned_technician.full_name if self.assigned_technician else None
def __repr__(self) -> str: def __repr__(self) -> str:
return f"<Ticket {self.ticket_number} ({self.status})>" return f"<Ticket {self.ticket_number} ({self.status})>"
+1
View File
@@ -92,6 +92,7 @@ class TicketBrief(BaseModel):
unit_id: int | None = None unit_id: int | None = None
category_id: int | None = None category_id: int | None = None
assigned_to: int | None = None assigned_to: int | None = None
assigned_technician_name: str | None = None
reporter: str | None = None reporter: str | None = None
description: str | None = None description: str | None = None
sla_deadline: datetime | None = None sla_deadline: datetime | None = None
+3 -3
View File
@@ -196,7 +196,7 @@ async def list_tickets(
# Paginate # Paginate
offset = (page - 1) * page_size offset = (page - 1) * page_size
query = query.order_by(Ticket.created_at.desc()).offset(offset).limit(page_size) query = query.order_by(Ticket.created_at.desc()).offset(offset).limit(page_size).options(selectinload(Ticket.assigned_technician))
result = await db.execute(query) result = await db.execute(query)
tickets = list(result.scalars().all()) tickets = list(result.scalars().all())
@@ -214,8 +214,8 @@ async def update_ticket(
# Handle status transitions separately # Handle status transitions separately
new_status = data.get("status") new_status = data.get("status")
if new_status is not None:
old_status = ticket.status old_status = ticket.status
if new_status is not None:
if old_status != new_status: if old_status != new_status:
valid_targets = VALID_TRANSITIONS.get(old_status, []) valid_targets = VALID_TRANSITIONS.get(old_status, [])
if new_status not in valid_targets: if new_status not in valid_targets:
@@ -277,7 +277,7 @@ async def update_ticket(
# Handle standalone note (no status change) # Handle standalone note (no status change)
note_only = data.get("note") note_only = data.get("note")
if note_only and (new_status is None or new_status == ticket.status): if note_only and not (new_status is not None and old_status != new_status):
await _log_status_change( await _log_status_change(
db, db,
ticket.id, ticket.id,
+6 -9
View File
@@ -203,16 +203,13 @@
// Emergency // Emergency
this.emergencyCount = active.filter(t => t.priority === 'urgent').length; this.emergencyCount = active.filter(t => t.priority === 'urgent').length;
// East vs West — we need full tickets with unit info // East vs West — compute from loaded tickets using unit map
// For now, estimate from overall data or show placeholder
// We'll load again with property filter or just use total
try { try {
const east = await app().apiGet('/api/tickets?property=East&page_size=1'); const units = await app().apiGet('/api/tickets/units');
const west = await app().apiGet('/api/tickets?property=West&page_size=1'); const unitMap = {};
const eastTotal = east?.total || 0; if (units) units.forEach(u => { unitMap[u.id] = u.property; });
const westTotal = west?.total || 0; this.kpi.eastJobs = active.filter(t => t.unit_id && unitMap[t.unit_id] === 'East').length;
this.kpi.eastJobs = eastTotal; this.kpi.westJobs = active.filter(t => t.unit_id && unitMap[t.unit_id] === 'West').length;
this.kpi.westJobs = westTotal;
} catch (e) { console.error('Property stats error', e); } } catch (e) { console.error('Property stats error', e); }
// Tech workload (simulated from assigned_to counts) // Tech workload (simulated from assigned_to counts)