no-mistakes(review): Fix F01/F02/F05: technician name, duplicate timeline, FM dashboard property counts
This commit is contained in:
@@ -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})>"
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user