winofy-backend/apps/stores/models.py
Ali Asadi f7a65cb902 Return store latitude/longitude in every store response
Add Store.latitude/Store.longitude properties derived from the
location PointField, and expose them read-only on StoreListSerializer
(inherited by StoreDetailSerializer). Also make them readable on
SellerStoreSerializer, which previously only accepted them as
write-only input.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 16:14:33 +03:30

128 lines
5 KiB
Python

from django.conf import settings
from django.contrib.gis.db import models as gis_models
from django.core.validators import RegexValidator
from django.db import models
from apps.locations.models import City, Neighborhood
from utils.models import BaseModel
class StoreCategory(BaseModel):
name = models.CharField(max_length=100, unique=True)
icon = models.CharField(max_length=500, null=True, blank=True, help_text='MinIO object key.')
order = models.PositiveSmallIntegerField(default=0)
class Meta:
ordering = ['order', 'name']
verbose_name = 'دسته‌بندی فروشگاه'
verbose_name_plural = 'دسته‌بندی‌های فروشگاه'
def __str__(self):
return self.name
class Store(BaseModel):
class Status(models.TextChoices):
PENDING = 'pending', 'در انتظار تایید'
APPROVED = 'approved', 'تایید شده'
SUSPENDED = 'suspended', 'معلق شده'
owner = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name='store')
category = models.ForeignKey(StoreCategory, on_delete=models.PROTECT, related_name='stores')
name = models.CharField(max_length=150)
description = models.TextField(blank=True)
logo = models.CharField(max_length=500, null=True, blank=True, help_text='MinIO object key.')
cover_image = models.CharField(max_length=500, null=True, blank=True, help_text='MinIO object key.')
phone_number = models.CharField(max_length=20, blank=True)
city = models.ForeignKey(City, on_delete=models.PROTECT, related_name='stores')
address = models.TextField()
location = gis_models.PointField(geography=True, null=True, blank=True)
service_neighborhoods = models.ManyToManyField(
Neighborhood, related_name='stores', blank=True,
help_text='محله‌هایی که این فروشگاه به آن‌ها ارسال دارد.',
)
delivery_radius_km = models.DecimalField(max_digits=5, decimal_places=2, default=5)
min_order_amount = models.PositiveBigIntegerField(default=0)
delivery_fee = models.PositiveBigIntegerField(default=0)
free_delivery_threshold = models.PositiveBigIntegerField(null=True, blank=True)
commission_percent = models.DecimalField(
max_digits=5, decimal_places=2, null=True, blank=True,
help_text='در صورت خالی بودن، از DEFAULT_COMMISSION_PERCENT استفاده می‌شود.',
)
accepts_wallet = models.BooleanField(default=True)
accepts_online = models.BooleanField(default=True)
accepts_cash_on_delivery = models.BooleanField(default=True)
rating_avg = models.DecimalField(max_digits=3, decimal_places=2, default=0)
rating_count = models.PositiveIntegerField(default=0)
status = models.CharField(max_length=10, choices=Status.choices, default=Status.PENDING)
is_open = models.BooleanField(default=True)
class Meta:
ordering = ['-created_at']
verbose_name = 'فروشگاه'
verbose_name_plural = 'فروشگاه‌ها'
def __str__(self):
return self.name
@property
def commission_percent_effective(self):
if self.commission_percent is not None:
return self.commission_percent
from django.conf import settings as dj_settings
return dj_settings.DEFAULT_COMMISSION_PERCENT
@property
def latitude(self):
return self.location.y if self.location else None
@property
def longitude(self):
return self.location.x if self.location else None
class StoreWorkingHours(BaseModel):
class Weekday(models.IntegerChoices):
SATURDAY = 0, 'شنبه'
SUNDAY = 1, 'یکشنبه'
MONDAY = 2, 'دوشنبه'
TUESDAY = 3, 'سه‌شنبه'
WEDNESDAY = 4, 'چهارشنبه'
THURSDAY = 5, 'پنجشنبه'
FRIDAY = 6, 'جمعه'
store = models.ForeignKey(Store, on_delete=models.CASCADE, related_name='working_hours')
weekday = models.PositiveSmallIntegerField(choices=Weekday.choices)
opens_at = models.TimeField(null=True, blank=True)
closes_at = models.TimeField(null=True, blank=True)
is_closed = models.BooleanField(default=False)
class Meta:
ordering = ['weekday']
unique_together = ('store', 'weekday')
verbose_name = 'ساعت کاری فروشگاه'
verbose_name_plural = 'ساعات کاری فروشگاه'
def __str__(self):
return f'{self.store.name} - {self.get_weekday_display()}'
class StoreBankAccount(BaseModel):
store = models.OneToOneField(Store, on_delete=models.CASCADE, related_name='bank_account')
iban = models.CharField(
max_length=26,
validators=[RegexValidator(r'^IR\d{24}$', 'شماره شبا معتبر نیست.')],
)
account_holder_name = models.CharField(max_length=150)
class Meta:
verbose_name = 'حساب بانکی فروشگاه'
verbose_name_plural = 'حساب‌های بانکی فروشگاه'
def __str__(self):
return f'{self.store.name} - {self.iban}'