Spaces:
Sleeping
Sleeping
Ticket Reactivation Strategy - Final Design
Overview
The ticket system now supports space-efficient reactivation of cancelled/completed tickets, while still maintaining unique deduplication keys for audit trails.
Business Problem Solved
Scenario: Customer Service Reactivation
Customer orders WiFi β Cancels β Wants WiFi again
Bad Design (Create New Ticket):
- β Duplicate rows in database
- β Lose payment history if customer paid before cancelling
- β Can't track chronic cancellers
- β Wastes database space
Good Design (Reactivate Ticket):
- β Reuse existing ticket row (space efficient)
- β Preserve payment history
- β Track customer behavior (chronic cancellers)
- β Complete audit trail
Implementation Logic
Create Ticket Flow (With Reactivation)
def create_ticket_from_sales_order(sales_order_id):
"""
Creates new ticket OR reactivates cancelled/completed ticket.
"""
# 1. Check for ACTIVE ticket (prevent duplicates)
active_ticket = find_active_ticket(sales_order_id)
if active_ticket:
raise HTTPException(409, "Active ticket already exists")
# 2. Check for CANCELLED/COMPLETED ticket (reactivation opportunity)
cancelled_ticket = find_cancelled_ticket(sales_order_id)
if cancelled_ticket:
# REACTIVATE: Update existing ticket
cancelled_ticket.status = "open"
cancelled_ticket.scheduled_date = new_date
cancelled_ticket.notes = f"[REACTIVATED {now}] {notes}\n{old_notes}"
# dedup_key remains unchanged (permanent)
return cancelled_ticket # β
Reused row
# 3. No existing ticket - Create new one
dedup_key = MD5(source + source_id + ticket_type + created_at)
new_ticket = Ticket(
source_id=sales_order_id,
status="open",
dedup_key=dedup_key # Permanent unique key
)
return new_ticket # β
New row
Key Points
- Active Ticket Check: Prevents duplicate active tickets
- Cancelled Ticket Check: Enables reactivation (space efficient)
- Dedup Key: Permanent (tracks creation instance, not lifecycle)
- Reactivation: Updates status, dates, adds note, reuses row
Business Rules
Rule 1: One Source = One Active Ticket
- Sales order can have ONE active ticket at a time
- Prevents duplicate work orders
- Check:
status NOT IN ('completed', 'cancelled')
Rule 2: Reactivation for Same Service
Customer cancels 10Mbps WiFi β Wants 10Mbps WiFi again
β REACTIVATE cancelled ticket (reuse row)
Implementation:
- Find cancelled ticket for same sales order
- Update status: cancelled β open
- Update dates and schedule
- Add reactivation note with timestamp
- Dedup key remains unchanged
Rule 3: New Ticket for Different Service
Customer had 10Mbps (cancelled) β Now wants 50Mbps
β Create NEW sales order β NEW ticket (new row)
Implementation:
- Create new sales order (different package)
- Create new ticket with new dedup_key
- History shows multiple tickets for customer
Database Space Efficiency
Scenario: Chronic Canceller
Without Reactivation (Wasteful):
-- Customer orders/cancels 5 times
INSERT INTO tickets (...) -- Ticket 1
INSERT INTO tickets (...) -- Ticket 2
INSERT INTO tickets (...) -- Ticket 3
INSERT INTO tickets (...) -- Ticket 4
INSERT INTO tickets (...) -- Ticket 5
-- Result: 5 rows for same sales order
With Reactivation (Efficient):
-- Customer orders/cancels 5 times
INSERT INTO tickets (...) -- Ticket 1 created
UPDATE tickets SET status='cancelled' -- Cancelled
UPDATE tickets SET status='open' -- Reactivated
UPDATE tickets SET status='cancelled' -- Cancelled again
UPDATE tickets SET status='open' -- Reactivated again
-- Result: 1 row for sales order
-- Notes field tracks all state changes
Savings:
- 1 row instead of 5
- 80% space reduction
- Payment history preserved
- Chronic canceller easily identified
Dedup Key Strategy
Format
dedup_key = MD5(source + '::' + source_id + '::' + ticket_type + '::' + created_at)
Properties
- Permanent: Never cleared (even on completion/cancellation)
- Unique: Timestamp ensures uniqueness
- Tracks Creation: Identifies when ticket was first created
- Not Lifecycle: Doesn't change with status updates
Why Timestamp?
- Prevents Collisions: If same source tries multiple times
- Audit Trail: Know when ticket was created
- Allows Multiple Tickets: New creation = new timestamp = new dedup_key
- Reactivation Support: Same dedup_key when reactivating
API Behavior
POST /api/v1/tickets/from-sales-order
Request:
{
"sales_order_id": "uuid-123",
"priority": "normal",
"scheduled_date": "2024-03-20",
"notes": "Customer wants morning installation"
}
Response (New Ticket):
{
"id": "ticket-uuid",
"source": "sales_order",
"source_id": "uuid-123",
"status": "open",
"dedup_key": "abc123...",
"created_at": "2024-03-15T10:00:00Z"
}
Response (Reactivated Ticket):
{
"id": "ticket-uuid",
"source": "sales_order",
"source_id": "uuid-123",
"status": "open", // Changed from 'cancelled'
"dedup_key": "abc123...", // Same as before
"created_at": "2024-03-10T08:00:00Z", // Original creation date
"updated_at": "2024-03-15T10:00:00Z", // Now
"notes": "[REACTIVATED 2024-03-15T10:00:00Z] Customer wants morning installation\n[CANCELLED 2024-03-12] Customer changed mind\n..."
}
Customer Behavior Tracking
Identifying Chronic Cancellers
Query:
-- Find tickets that were reactivated multiple times
SELECT
ticket_name,
status,
notes,
(LENGTH(notes) - LENGTH(REPLACE(notes, '[REACTIVATED', ''))) / LENGTH('[REACTIVATED') as reactivation_count
FROM tickets
WHERE notes LIKE '%[REACTIVATED%'
ORDER BY reactivation_count DESC;
Business Value:
- Identify customers who frequently cancel
- Adjust payment terms (require upfront payment)
- Risk assessment for resource allocation
- Customer support insights
Payment History Preservation
Scenario: Customer Paid Before Cancelling
Without Reactivation:
Ticket 1 (cancelled) β Has payment record
Ticket 2 (new) β No link to previous payment
β Lost payment history! β
With Reactivation:
Ticket 1 (cancelled) β Has payment record
Ticket 1 (reactivated) β Still has payment record
β Payment history preserved! β
Additional Metadata Example:
{
"payments": [
{
"amount": 5000,
"date": "2024-03-01",
"status": "refunded",
"reason": "Customer cancelled before installation"
}
],
"cancellations": [
{
"date": "2024-03-05",
"reason": "Customer changed mind"
}
],
"reactivations": [
{
"date": "2024-03-15",
"reason": "Customer wants service again"
}
]
}
Summary
Key Features
- β Space Efficient: Reactivates existing tickets instead of creating duplicates
- β Payment History: Preserves financial records when reactivating
- β Customer Behavior: Tracks chronic cancellers via reactivation notes
- β Audit Trail: Dedup key is permanent (tracks creation instance)
- β Flexibility: New service = new ticket, same service = reactivate
Database Impact
- Reduces rows: 1 ticket per sales order (not 1 per order attempt)
- Preserves data: Payment history, notes, metadata
- Clear history: Notes field shows all state changes with timestamps
Business Value
- Cost Savings: Less database storage
- Better Analytics: True customer behavior tracking
- Financial Accuracy: Payment history never lost
- Operational Efficiency: One ticket to manage per order
Migration Notes
No database migration needed!
The existing schema already supports this:
dedup_keyis already unique and permanentstatuscolumn supports state transitionsnotesfield stores reactivation history- Logic change only (service layer, not schema)
Code Changes:
- β Service: Check for cancelled tickets before creating new
- β Service: Reactivate logic (update status, dates, notes)
- β API: Document reactivation behavior
- β Docs: Update business rules