Django 6 + DRF resource server against the Gooyal accounts OAuth2 service, matching the Winsoo ecosystem's conventions. Covers locations, stores, catalog, cart, checkout/orders (with the multi-store-cart split and the status stepper), reviews, and notifications, plus a demo-data seed command. Payment integration (wallet debits, online gateway, seller payouts) is intentionally left out here — see feature/payment. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
60 lines
2 KiB
Python
60 lines
2 KiB
Python
from django.db import models
|
|
|
|
from apps.stores.models import Store
|
|
from utils.models import BaseModel
|
|
|
|
|
|
class ProductCategory(BaseModel):
|
|
name = models.CharField(max_length=100)
|
|
parent = models.ForeignKey(
|
|
'self', on_delete=models.CASCADE, related_name='children', null=True, blank=True,
|
|
)
|
|
icon = models.ImageField(upload_to='product_categories/', null=True, blank=True)
|
|
order = models.PositiveSmallIntegerField(default=0)
|
|
|
|
class Meta:
|
|
ordering = ['order', 'name']
|
|
verbose_name = 'دستهبندی محصول'
|
|
verbose_name_plural = 'دستهبندیهای محصول'
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
class Product(BaseModel):
|
|
class UnitType(models.TextChoices):
|
|
GRAM = 'gram', 'گرم'
|
|
MILLILITER = 'ml', 'میلیلیتر'
|
|
PIECE = 'piece', 'عدد'
|
|
|
|
store = models.ForeignKey(Store, on_delete=models.CASCADE, related_name='products')
|
|
category = models.ForeignKey(ProductCategory, on_delete=models.PROTECT, related_name='products')
|
|
name = models.CharField(max_length=200)
|
|
description = models.TextField(blank=True)
|
|
image = models.ImageField(upload_to='products/', null=True, blank=True)
|
|
|
|
price = models.PositiveBigIntegerField()
|
|
unit_type = models.CharField(max_length=10, choices=UnitType.choices, default=UnitType.PIECE)
|
|
unit_value = models.PositiveIntegerField(null=True, blank=True)
|
|
|
|
stock_quantity = models.PositiveIntegerField(default=0)
|
|
low_stock_threshold = models.PositiveIntegerField(default=5)
|
|
|
|
is_active = models.BooleanField(default=True)
|
|
sold_count = models.PositiveIntegerField(default=0)
|
|
|
|
class Meta:
|
|
ordering = ['-created_at']
|
|
verbose_name = 'محصول'
|
|
verbose_name_plural = 'محصولات'
|
|
|
|
def __str__(self):
|
|
return f'{self.name} - {self.store.name}'
|
|
|
|
@property
|
|
def is_out_of_stock(self):
|
|
return self.stock_quantity <= 0
|
|
|
|
@property
|
|
def is_low_stock(self):
|
|
return 0 < self.stock_quantity <= self.low_stock_threshold
|