a.1 Solution A: ภาพรวม
a.1 Solution A: ภาพรวม
แนวทาง: แก้เฉพาะ Flow 2 (Refund/Void) — ไม่เพิ่ม schema, ไม่แตะ Flow อื่น
สรุปแบบภาษาคน
ระบบเดิมเมื่อมีการคืนสินค้า จะคำนวณยอดสะสมใหม่โดยดูย้อนหลัง 24 เดือนจาก “วันนี้” ทำให้ลง Tier ง่ายเกินไป แม้แต่ refund ที่เกิดหลังจากขึ้น Tier ก็ยังทำให้ลงได้
Solution A แก้เฉพาะ การลง Tier จาก void/refund โดยใช้ window การคำนวณใหม่ที่ยึดจาก “วันที่ขึ้น Tier” แทน “วันนี้” และเพิ่ม guard ว่า refund ที่เกิดหลัง up-tier ไม่ทำให้ลง Tier
การขึ้น Tier ยังใช้ logic เดิมทุกอย่าง ไม่เปลี่ยน
Scope: ขึ้น Tier vs ลง Tier
| ขึ้น Tier (upgrade) | ลง Tier (downgrade จาก void/refund) | |
|---|---|---|
| Logic | เดิม — ไม่เปลี่ยน | ใหม่ — ตามกฎ 6 ข้อ |
| ใช้ยอดสะสม | accumulateSpending (rolling 24 เดือน) |
Accum Tier Logic (window พิเศษ) |
| Function | calculateTierToUpgrade() — ไม่แก้ |
addNewRefundSalesTransaction() — แก้ |
| Trigger | ซื้อของ (Flow 1) | คืนสินค้า / ยกเลิกรายการ (Flow 2) |
Accum Tier Logic (window ใหม่)
Accum Tier Logic = SUM 24 เดือนย้อนหลังจาก up-tier date
+ SUM ทุก transaction หลัง up-tier จนถึงปัจจุบัน
ต่างจาก Legacy ที่ใช้ rolling 24 เดือนจากวันนี้ ซึ่งทำให้ยอดลดลงเรื่อยๆ ตามเวลา
กฎ 6 ข้อ (สรุป)
| ข้อ | กฎ | ผลลัพธ์ |
|---|---|---|
| 1 | Transaction expired ไม่ทำให้ down-tier | ไม่ down-tier เพราะไม่มี refund event |
| 2 | Void/Refund หลัง up-tier ไม่ทำให้ down-tier | Guard: originalSale.completedAt > upTierDate → return |
| 3 | Void/Refund ก่อน/ตอน up-tier → ดู Accum Tier Logic | คำนวณใหม่ด้วย window พิเศษ แล้วดูว่าถึง tier ไหน |
| 4 | Maintain spending คำนวณใหม่เมื่อ down-tier | ไม่ reset = 0 แต่ SUM txns หลัง up-tier |
| 5 | tierStartedAt/tierEndedAt ตาม history | ดู validity ของ tier ก่อนหน้า (5.1) หรือคำนวณใหม่ (5.2) |
| 6 | Admin DEDUCT ใช้ logic เดียวกับ Refund | delegate ไป Flow 2 อัตโนมัติ |
สิ่งที่เปลี่ยน vs ไม่เปลี่ยน
| Flow | เปลี่ยน? | หมายเหตุ |
|---|---|---|
| Flow 1 — Sales | ❌ | ไม่เปลี่ยน 100% |
| Flow 2 — Refund/Void | ✅ | เปลี่ยน guard + down-tier logic ทั้งหมด |
| Flow 3A — Batch Accum | ❌ | ไม่เปลี่ยน |
| Flow 3B — Batch Recalc | ❌ | ไม่เปลี่ยน |
| Flow 4 — Maintain Tier | ❌ | ไม่เปลี่ยน |
| Flow 5 — Reconcile | ❌ | ไม่เปลี่ยน |
| Flow 6 — Admin ADD | ❌ | delegate ไป Flow 1 → ไม่เปลี่ยน |
| Flow 6 — Admin DEDUCT | ⚠️ | delegate ไป Flow 2 → ได้รับผลทางอ้อม |
| Flow 7 — Co-Brand | ❌ | ไม่เปลี่ยน |
| Flow 8 — Staff Exit | ❌ | ไม่เปลี่ยน |
| Flow 9 — Member Import | ❌ | ไม่เปลี่ยน |
Key Methods ใหม่
| Method | วัตถุประสงค์ |
|---|---|
calculateAccumTierLogicSpending() |
คำนวณ Accum Tier Logic = SUM(24mo ก่อน up-tier) + SUM(ทุก txn หลัง up-tier) |
calculateTierAdjustmentWithNewLogic() |
ตัดสิน down-tier จาก Accum Tier Logic แทน rolling 24mo + apply co-brand floor + cap |
capToCurrentTier() |
cap ไม่ให้ tier ปลายทางสูงกว่า tier ปัจจุบัน (flow นี้เป็น down-tier เท่านั้น) |
calculatePostUpTierMaintainSpending() |
คำนวณ maintain จาก txns หลัง up-tier (ไม่รวม up-tier txn) — ใช้ทั้ง down-tier และ tier-ไม่เปลี่ยน |
findPreviousTierInfo() |
หา tier dates จาก MemberTierHistory ที่ยัง valid (tierEndedAt ยังไม่หมดอายุ) |
findQualifyingTransactionDate() |
scan หา txn ที่ทำให้ accum (หักลบ refund) ข้าม threshold ใน window [24mo ก่อน up-tier, now]; return null ถ้า threshold ≤ 0 |
ทำไม Solution A ถึง impact น้อยสุด
| Aspect | Solution A |
|---|---|
| Files ที่แก้ | 2 files (member.entity.ts, refund-sales-transaction.service.ts) |
| Functions ที่แก้ | 1 existing (addNewRefundSalesTransaction) + 5 new private methods |
| Schema change | ไม่มี |
| Flows ที่กระทบ | แค่ Flow 2 |
| Regression risk | ต่ำมาก |
อ่านรายละเอียด sequence diagram แต่ละ flow ใน
solution-a-sequence-diagrams.md
อ่าน implementation plan ในsolution-a-implementation-plan.md