84 lines
2.6 KiB
Python
84 lines
2.6 KiB
Python
"""Mesin status order (FR-ORD-04, FR-ORD-09, FR-ORD-10, UC-07, UC-08)."""
|
|
|
|
from datetime import timedelta
|
|
from enum import Enum
|
|
|
|
AUTO_COMPLETE_AFTER = timedelta(days=3)
|
|
|
|
|
|
class OrderStatus(Enum):
|
|
PENDING_PAYMENT = "PENDING_PAYMENT"
|
|
PAID = "PAID"
|
|
SHIPPED = "SHIPPED"
|
|
COMPLETED = "COMPLETED"
|
|
CANCELLED = "CANCELLED"
|
|
EXPIRED = "EXPIRED"
|
|
|
|
|
|
class InvalidTransitionError(Exception):
|
|
"""Event tidak diizinkan pada status order saat ini."""
|
|
|
|
|
|
class PermissionDeniedError(Exception):
|
|
"""Pelaku tidak berhak memicu event ini."""
|
|
|
|
|
|
S = OrderStatus
|
|
|
|
# Tabel transisi: (status asal, event) -> status tujuan. Pasangan lain ditolak.
|
|
TRANSITIONS = {
|
|
(S.PENDING_PAYMENT, "pay"): S.PAID,
|
|
(S.PENDING_PAYMENT, "cancel"): S.CANCELLED,
|
|
(S.PENDING_PAYMENT, "expire"): S.EXPIRED,
|
|
(S.PAID, "ship"): S.SHIPPED,
|
|
(S.SHIPPED, "complete"): S.COMPLETED,
|
|
}
|
|
|
|
|
|
class Order:
|
|
def __init__(self, order_id, buyer_id, seller_id):
|
|
self.order_id = order_id
|
|
self.buyer_id = buyer_id
|
|
self.seller_id = seller_id
|
|
self.status = S.PENDING_PAYMENT
|
|
self.shipped_at = None
|
|
|
|
def _fire(self, event):
|
|
target = TRANSITIONS.get((self.status, event))
|
|
if target is None:
|
|
raise InvalidTransitionError(f"{event} tidak valid pada status {self.status.value}")
|
|
self.status = target
|
|
|
|
def pay(self):
|
|
"""Event payment.succeeded dari Payment Service."""
|
|
self._fire("pay")
|
|
|
|
def cancel(self, actor_id):
|
|
"""Pembeli membatalkan order yang belum dibayar."""
|
|
if actor_id != self.buyer_id:
|
|
raise PermissionDeniedError("hanya pembeli yang dapat membatalkan")
|
|
self._fire("cancel")
|
|
|
|
def expire(self):
|
|
"""Batas waktu pembayaran habis (dipicu sistem)."""
|
|
self._fire("expire")
|
|
|
|
def ship(self, actor_id, now):
|
|
"""Seller pemilik toko menandai order terkirim."""
|
|
if actor_id != self.seller_id:
|
|
raise PermissionDeniedError("hanya seller pemilik toko yang dapat mengirim")
|
|
self._fire("ship")
|
|
self.shipped_at = now
|
|
|
|
def confirm_received(self, actor_id):
|
|
"""Pembeli mengonfirmasi pesanan diterima. Seller tidak boleh."""
|
|
if actor_id != self.buyer_id:
|
|
raise PermissionDeniedError("hanya pembeli yang dapat mengonfirmasi")
|
|
self._fire("complete")
|
|
|
|
def auto_complete(self, now):
|
|
"""Sistem menyelesaikan order 3 hari setelah SHIPPED tanpa konfirmasi."""
|
|
if self.status is S.SHIPPED and now - self.shipped_at < AUTO_COMPLETE_AFTER:
|
|
raise InvalidTransitionError("belum 3 hari sejak SHIPPED")
|
|
self._fire("complete")
|