from django.conf import settings from django.db import models from apps.catalog.models import Product from apps.stores.models import Store from utils.models import BaseModel class Cart(BaseModel): """Pre-checkout state — one per user, may hold items from multiple stores.""" user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='cart') class Meta: verbose_name = 'سبد خرید' verbose_name_plural = 'سبدهای خرید' def __str__(self): return f'سبد خرید {self.user}' class CartItem(BaseModel): cart = models.ForeignKey(Cart, on_delete=models.CASCADE, related_name='items') product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='+') quantity = models.PositiveIntegerField(default=1) class Meta: unique_together = ('cart', 'product') verbose_name = 'قلم سبد خرید' verbose_name_plural = 'اقلام سبد خرید' def __str__(self): return f'{self.product.name} x{self.quantity}' @property def line_total(self): return self.product.price * self.quantity class OrderGroup(BaseModel): """One checkout/payment event — fans out into one Order per store (multi-store-cart).""" class DeliveryType(models.TextChoices): EXPRESS = 'express', 'ارسال فوری' SCHEDULED = 'scheduled', 'زمان‌بندی شده' class PaymentMethod(models.TextChoices): WALLET = 'wallet', 'کیف پول وینسو' ONLINE = 'online', 'درگاه بانکی' CASH_ON_DELIVERY = 'cash_on_delivery', 'پرداخت در محل' class PaymentStatus(models.TextChoices): PENDING = 'pending', 'در انتظار پرداخت' PAID = 'paid', 'پرداخت شده' FAILED = 'failed', 'ناموفق' user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name='order_groups') recipient_name = models.CharField(max_length=150) recipient_phone = models.CharField(max_length=20) full_address = models.TextField() delivery_type = models.CharField(max_length=10, choices=DeliveryType.choices, default=DeliveryType.EXPRESS) scheduled_at = models.DateTimeField(null=True, blank=True) payment_method = models.CharField(max_length=20, choices=PaymentMethod.choices) payment_status = models.CharField(max_length=10, choices=PaymentStatus.choices, default=PaymentStatus.PENDING) notes = models.TextField(blank=True) total_amount = models.PositiveBigIntegerField() class Meta: ordering = ['-created_at'] verbose_name = 'گروه سفارش' verbose_name_plural = 'گروه‌های سفارش' def __str__(self): return f'گروه سفارش {self.uuid} - {self.user}' class Order(BaseModel): class Status(models.TextChoices): PLACED = 'placed', 'سفارش ثبت شد' PREPARING = 'preparing', 'در حال آماده‌سازی' READY_TO_SHIP = 'ready_to_ship', 'آماده ارسال' HANDED_TO_COURIER = 'handed_to_courier', 'تحویل سفیر شد' DELIVERED = 'delivered', 'تحویل داده شد' CANCELLED = 'cancelled', 'لغو شده' CANCELLABLE_STATUSES = (Status.PLACED, Status.PREPARING) group = models.ForeignKey(OrderGroup, on_delete=models.PROTECT, related_name='orders') store = models.ForeignKey(Store, on_delete=models.PROTECT, related_name='orders') customer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name='orders') status = models.CharField(max_length=20, choices=Status.choices, default=Status.PLACED) items_subtotal = models.PositiveBigIntegerField() delivery_fee = models.PositiveBigIntegerField(default=0) commission_amount = models.PositiveBigIntegerField(default=0) seller_payout_amount = models.PositiveBigIntegerField(default=0) cancel_reason = models.TextField(blank=True) courier_name = models.CharField(max_length=150, blank=True) courier_phone = models.CharField(max_length=20, blank=True) placed_at = models.DateTimeField(auto_now_add=True) delivered_at = models.DateTimeField(null=True, blank=True) class Meta: ordering = ['-created_at'] verbose_name = 'سفارش' verbose_name_plural = 'سفارش‌ها' def __str__(self): return f'سفارش {self.uuid} - {self.store.name}' @property def total_amount(self): return self.items_subtotal + self.delivery_fee class OrderItem(BaseModel): order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items') product = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True, related_name='+') product_name_snapshot = models.CharField(max_length=200) unit_price_snapshot = models.PositiveBigIntegerField() quantity = models.PositiveIntegerField() class Meta: verbose_name = 'قلم سفارش' verbose_name_plural = 'اقلام سفارش' def __str__(self): return f'{self.product_name_snapshot} x{self.quantity}' @property def line_total(self): return self.unit_price_snapshot * self.quantity class OrderStatusLog(BaseModel): order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='status_logs') from_status = models.CharField(max_length=20, blank=True) to_status = models.CharField(max_length=20) changed_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='+', ) note = models.TextField(blank=True) class Meta: ordering = ['created_at'] verbose_name = 'تاریخچه وضعیت سفارش' verbose_name_plural = 'تاریخچه وضعیت سفارش‌ها' def __str__(self): return f'{self.order_id}: {self.from_status} -> {self.to_status}' class Notification(BaseModel): class Type(models.TextChoices): NEW_ORDER = 'new_order', 'سفارش جدید' SETTLEMENT_DONE = 'settlement_done', 'تسویه حساب' NEW_REVIEW = 'new_review', 'نظر جدید' ORDER_CANCELLED = 'order_cancelled', 'لغو سفارش' recipient = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='notifications') type = models.CharField(max_length=20, choices=Type.choices) title = models.CharField(max_length=200) body = models.TextField(blank=True) related_order = models.ForeignKey( Order, on_delete=models.SET_NULL, null=True, blank=True, related_name='notifications', ) is_read = models.BooleanField(default=False) class Meta: ordering = ['-created_at'] verbose_name = 'اعلان' verbose_name_plural = 'اعلان‌ها' def __str__(self): return f'{self.title} - {self.recipient}'