Files
2026-11-02 14:58:43 +07:00

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")