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