Spaces:
Sleeping
Sleeping
File size: 8,385 Bytes
38ac151 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
# 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
|