swiftops-backend / docs /agent /implementation-notes /TICKET_REACTIVATION_STRATEGY.md
kamau1's picture
feat(project): add complete project setup workflow with service methods and API endpoints for regions, roles, subcontractors, and finalization including validation and authorization
4835b24
# 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)
```python
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
1. **Active Ticket Check**: Prevents duplicate active tickets
2. **Cancelled Ticket Check**: Enables reactivation (space efficient)
3. **Dedup Key**: Permanent (tracks creation instance, not lifecycle)
4. **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):**
```sql
-- 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):**
```sql
-- 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?
1. **Prevents Collisions**: If same source tries multiple times
2. **Audit Trail**: Know when ticket was created
3. **Allows Multiple Tickets**: New creation = new timestamp = new dedup_key
4. **Reactivation Support**: Same dedup_key when reactivating
---
## API Behavior
### POST /api/v1/tickets/from-sales-order
**Request:**
```json
{
"sales_order_id": "uuid-123",
"priority": "normal",
"scheduled_date": "2024-03-20",
"notes": "Customer wants morning installation"
}
```
**Response (New Ticket):**
```json
{
"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):**
```json
{
"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:**
```sql
-- 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:**
```json
{
"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
1. βœ… **Space Efficient**: Reactivates existing tickets instead of creating duplicates
2. βœ… **Payment History**: Preserves financial records when reactivating
3. βœ… **Customer Behavior**: Tracks chronic cancellers via reactivation notes
4. βœ… **Audit Trail**: Dedup key is permanent (tracks creation instance)
5. βœ… **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_key` is already unique and permanent
- `status` column supports state transitions
- `notes` field 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