Files
support_backend/specs/011-agent-ticket-queue/data-model.md
T
saqib mirandClaude Sonnet 5 23fadebb5c docs(011-agent-ticket-queue): plan, research, data model, contract, quickstart
Extends the existing PATCH /admin/agents/:agentId with an optional userId
to finish wiring 010's Agent.userId link, and adds GET /agents/me/tickets
+ GET /admin/agents/:agentId/tickets sharing one ticketing/tickets service
method, backed by a new Assignment @@index([agentId, isCurrent]).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-07 12:58:25 +05:30

2.4 KiB

Data Model: Agent Ticket Queue

Modified: Agent

No new column — userId/user already exist (010-identity-auth). This feature is the first to actually write userId through an endpoint, and adds the supporting index below.

model Assignment {
  // ...existing fields unchanged...

  @@index([ticketId, isCurrent])
  @@index([agentId, isCurrent])   // NEW — supports "current assignments for agent X"
  @@map("assignments")
}

New (response-shape only, no new table): AssignedTicketSummary

A read projection, not a persisted entity — assembled per-request from Ticket joined to its current Assignment, Product, CustomerReference, and (if present) SLARun.

Field Source Notes
id Ticket.id
code Ticket.code e.g. ACME-2026-0042
status Ticket.status One of the 12 lifecycle states (003's own state machine)
priority Ticket.priority Opaque string, as already modeled
severity Ticket.severity Opaque string, as already modeled
product Ticket.product { id, externalProductId, name }
customer Ticket.customer { externalUserId, externalTenantId } — no PII beyond what 002's own CustomerReference already stores
assignedAt Assignment.assignedAt The current assignment's start time
sla SLARun (nullable) { status, firstResponseDueAt, resolutionDueAt, breachedAt } or null if no SLARun exists yet for this ticket

Validation / Business Rules

  • Linking (PATCH /admin/agents/:agentId's new userId field):
    • The target User must exist and have role AGENT (FR-001).
    • No other Agent row may already have that userId (FR-001) — checked proactively before the write (research.md), not left to the database's own @unique constraint to reject.
    • userId: null explicitly unlinks (distinct from omitting the field, which leaves it unchanged — the existing updateAgentSchema pattern for optional fields).
  • Listing (GET /agents/me/tickets, GET /admin/agents/:agentId/tickets):
    • Only Assignment.isCurrent: true rows are considered (FR-003).
    • The agent-self route resolves agentId exclusively from request.user.idAgent.userId lookup — never from any request input (FR-004).
    • A session with no linked Agent row throws a specific NotFoundError ("No agent profile is linked to this account."), never an empty array (FR-006).