|
|
|
@ -99,12 +99,20 @@ class SupplyTarget(AbstractDateTimeModel): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Discount(AbstractDateTimeModel): |
|
|
|
class Discount(AbstractDateTimeModel): |
|
|
|
code = models.CharField(max_length=50, blank=True, unique=True, default=str(uuid.uuid4())) |
|
|
|
def upload_file_to(self, filename): |
|
|
|
valid_from = models.DateTimeField(auto_now_add=True, blank=True) |
|
|
|
return "discounts/{name}/{filename}".format(**{ |
|
|
|
valid_to = models.DateTimeField(default=now() + timedelta(days=7), blank=True) |
|
|
|
'name': self.name, |
|
|
|
value = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(100)], default=0, |
|
|
|
'filename': filename |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
name = models.CharField(_('Имя'), max_length=255) |
|
|
|
|
|
|
|
image = models.FileField(_('Изображение'), upload_to=upload_file_to, blank=True, null=True) |
|
|
|
|
|
|
|
code = models.CharField(_('Код'), max_length=50, blank=True, unique=True, default=str(uuid.uuid4())) |
|
|
|
|
|
|
|
valid_from = models.DateTimeField(_('Начало'), auto_now_add=True, blank=True) |
|
|
|
|
|
|
|
valid_to = models.DateTimeField(_('Конец'), default=now() + timedelta(days=7), blank=True) |
|
|
|
|
|
|
|
value = models.IntegerField(_('Процент'), validators=[MinValueValidator(0), MaxValueValidator(100)], default=0, |
|
|
|
help_text=_('Указываем целым числом. Пример: 30 = 30%')) |
|
|
|
help_text=_('Указываем целым числом. Пример: 30 = 30%')) |
|
|
|
active = models.BooleanField(default=True) |
|
|
|
active = models.BooleanField(_('Активная'), default=True) |
|
|
|
|
|
|
|
|
|
|
|
def __str__(self): |
|
|
|
def __str__(self): |
|
|
|
return self.code |
|
|
|
return self.code |
|
|
|
@ -171,71 +179,6 @@ class Offer(AbstractStatusModel): |
|
|
|
verbose_name_plural = _('Позиции') |
|
|
|
verbose_name_plural = _('Позиции') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------ Buying status --------------------------------------------------- # |
|
|
|
|
|
|
|
BUYING_STATUS_IN_CART = 25 |
|
|
|
|
|
|
|
BUYING_STATUS_PENDING = 50 |
|
|
|
|
|
|
|
BUYING_STATUS_PAID = 75 |
|
|
|
|
|
|
|
BUYING_STATUS_CHOICES = ( |
|
|
|
|
|
|
|
(BUYING_STATUS_IN_CART, _('В корзине')), |
|
|
|
|
|
|
|
(BUYING_STATUS_PENDING, _('Обрабатываеться')), |
|
|
|
|
|
|
|
(BUYING_STATUS_PAID, _('Оплаченно')) |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BUYING_DEFAULT_CHOICE = BUYING_STATUS_IN_CART |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BuyingManager(ActiveOnlyManager, models.Manager): |
|
|
|
|
|
|
|
def get_user_buyings(self, user): |
|
|
|
|
|
|
|
qs = self.get_queryset() |
|
|
|
|
|
|
|
return qs.filter(user=user).all() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_buying_total_price(self, user=None): |
|
|
|
|
|
|
|
qs = self.get_user_buyings(user) if user else self.get_queryset() |
|
|
|
|
|
|
|
return qs.aggregate(Sum('total_price')) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_buying_total_bonus_points(self, user=None): |
|
|
|
|
|
|
|
qs = self.get_user_buyings(user) if user else self.get_queryset() |
|
|
|
|
|
|
|
return qs.aggregate(Sum('bonus_points')) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_buying_total_cashback(self, user=None): |
|
|
|
|
|
|
|
qs = self.get_user_buyings(user) if user else self.get_queryset() |
|
|
|
|
|
|
|
return qs.select_related('buying_cashback').aggregate(Sum('amount')) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Buying(AbstractStatusModel): |
|
|
|
|
|
|
|
user = models.ForeignKey(get_user_model(), verbose_name=_('пользователь'), on_delete=models.CASCADE) |
|
|
|
|
|
|
|
offer = models.ForeignKey(Offer, verbose_name=_('позиция'), on_delete=models.CASCADE) |
|
|
|
|
|
|
|
bonus_points = models.IntegerField(_('бонусы'), validators=(MinValueValidator(0),)) |
|
|
|
|
|
|
|
status = models.SmallIntegerField(_('статус'), default=BUYING_DEFAULT_CHOICE, choices=BUYING_STATUS_CHOICES) |
|
|
|
|
|
|
|
amount = models.SmallIntegerField(_('колличество'), default=0) |
|
|
|
|
|
|
|
total_price = models.DecimalField(_('цена'), max_digits=10, decimal_places=2) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
active = BuyingManager() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@property |
|
|
|
|
|
|
|
def is_in_cart(self): |
|
|
|
|
|
|
|
return self.status == BUYING_STATUS_IN_CART |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@property |
|
|
|
|
|
|
|
def is_pending(self): |
|
|
|
|
|
|
|
return self.status == BUYING_STATUS_PENDING |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@property |
|
|
|
|
|
|
|
def is_paid(self): |
|
|
|
|
|
|
|
return self.status == BUYING_STATUS_PAID |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __str__(self): |
|
|
|
|
|
|
|
return "{product_name}({product_amount}) - {price}".format(**{ |
|
|
|
|
|
|
|
'product_name': self.offer.product.name, |
|
|
|
|
|
|
|
'product_amount': self.amount, |
|
|
|
|
|
|
|
'price': self.total_price |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Meta: |
|
|
|
|
|
|
|
verbose_name = _('Покупка') |
|
|
|
|
|
|
|
verbose_name_plural = _('Покупки') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
STATUS_GAINED = 0 |
|
|
|
STATUS_GAINED = 0 |
|
|
|
STATUS_SPENT = 100 |
|
|
|
STATUS_SPENT = 100 |
|
|
|
CASHBACK_STATUS_CHOICES = ( |
|
|
|
CASHBACK_STATUS_CHOICES = ( |
|
|
|
@ -285,7 +228,7 @@ ORDER_STATUS_DEFAULT = ORDER_STATUS_NEW |
|
|
|
|
|
|
|
|
|
|
|
class Order(AbstractStatusModel): |
|
|
|
class Order(AbstractStatusModel): |
|
|
|
order_code = models.CharField(_('код заказа'), max_length=255, default=str(uuid.uuid4())) |
|
|
|
order_code = models.CharField(_('код заказа'), max_length=255, default=str(uuid.uuid4())) |
|
|
|
customer_name = models.CharField(_('bмя'), max_length=255) |
|
|
|
customer_name = models.CharField(_('имя'), max_length=255) |
|
|
|
customer_email = models.EmailField(_('email'), blank=True, null=True, default=None) |
|
|
|
customer_email = models.EmailField(_('email'), blank=True, null=True, default=None) |
|
|
|
customer_user = models.ForeignKey( |
|
|
|
customer_user = models.ForeignKey( |
|
|
|
get_user_model(), on_delete=models.SET_NULL, |
|
|
|
get_user_model(), on_delete=models.SET_NULL, |
|
|
|
@ -302,7 +245,6 @@ class Order(AbstractStatusModel): |
|
|
|
customer_address = models.TextField(_('адрес')) |
|
|
|
customer_address = models.TextField(_('адрес')) |
|
|
|
city = models.ForeignKey(City, on_delete=models.PROTECT, verbose_name=_('Город')) |
|
|
|
city = models.ForeignKey(City, on_delete=models.PROTECT, verbose_name=_('Город')) |
|
|
|
|
|
|
|
|
|
|
|
buyings = models.ManyToManyField(Buying, verbose_name=_('Покупки')) |
|
|
|
|
|
|
|
total_price = models.DecimalField(_('стоимость'), max_digits=10, decimal_places=2, default=0) |
|
|
|
total_price = models.DecimalField(_('стоимость'), max_digits=10, decimal_places=2, default=0) |
|
|
|
comment = models.TextField(_('комментарий'), blank=True, null=True, default=None) |
|
|
|
comment = models.TextField(_('комментарий'), blank=True, null=True, default=None) |
|
|
|
status = models.SmallIntegerField(_('статус'), default=ORDER_STATUS_CHOICES, choices=ORDER_STATUS_DEFAULT) |
|
|
|
status = models.SmallIntegerField(_('статус'), default=ORDER_STATUS_CHOICES, choices=ORDER_STATUS_DEFAULT) |
|
|
|
@ -316,6 +258,72 @@ class Order(AbstractStatusModel): |
|
|
|
verbose_name_plural = _('Заказы') |
|
|
|
verbose_name_plural = _('Заказы') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------ Buying status --------------------------------------------------- # |
|
|
|
|
|
|
|
BUYING_STATUS_IN_CART = 25 |
|
|
|
|
|
|
|
BUYING_STATUS_PENDING = 50 |
|
|
|
|
|
|
|
BUYING_STATUS_PAID = 75 |
|
|
|
|
|
|
|
BUYING_STATUS_CHOICES = ( |
|
|
|
|
|
|
|
(BUYING_STATUS_IN_CART, _('В корзине')), |
|
|
|
|
|
|
|
(BUYING_STATUS_PENDING, _('Обрабатываеться')), |
|
|
|
|
|
|
|
(BUYING_STATUS_PAID, _('Оплаченно')) |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BUYING_DEFAULT_CHOICE = BUYING_STATUS_IN_CART |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BuyingManager(ActiveOnlyManager, models.Manager): |
|
|
|
|
|
|
|
def get_user_buyings(self, user): |
|
|
|
|
|
|
|
qs = self.get_queryset() |
|
|
|
|
|
|
|
return qs.filter(user=user).all() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_buying_total_price(self, user=None): |
|
|
|
|
|
|
|
qs = self.get_user_buyings(user) if user else self.get_queryset() |
|
|
|
|
|
|
|
return qs.aggregate(Sum('total_price')) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_buying_total_bonus_points(self, user=None): |
|
|
|
|
|
|
|
qs = self.get_user_buyings(user) if user else self.get_queryset() |
|
|
|
|
|
|
|
return qs.aggregate(Sum('bonus_points')) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_buying_total_cashback(self, user=None): |
|
|
|
|
|
|
|
qs = self.get_user_buyings(user) if user else self.get_queryset() |
|
|
|
|
|
|
|
return qs.select_related('buying_cashback').aggregate(Sum('amount')) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Buying(AbstractStatusModel): |
|
|
|
|
|
|
|
order = models.ForeignKey(Order, verbose_name=_('пользователь'), on_delete=models.CASCADE) |
|
|
|
|
|
|
|
user = models.ForeignKey(get_user_model(), verbose_name=_('пользователь'), on_delete=models.CASCADE) |
|
|
|
|
|
|
|
offer = models.ForeignKey(Offer, verbose_name=_('позиция'), on_delete=models.CASCADE) |
|
|
|
|
|
|
|
bonus_points = models.IntegerField(_('бонусы'), validators=(MinValueValidator(0),)) |
|
|
|
|
|
|
|
status = models.SmallIntegerField(_('статус'), default=BUYING_DEFAULT_CHOICE, choices=BUYING_STATUS_CHOICES) |
|
|
|
|
|
|
|
amount = models.SmallIntegerField(_('колличество'), default=0) |
|
|
|
|
|
|
|
total_price = models.DecimalField(_('цена'), max_digits=10, decimal_places=2) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
active = BuyingManager() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@property |
|
|
|
|
|
|
|
def is_in_cart(self): |
|
|
|
|
|
|
|
return self.status == BUYING_STATUS_IN_CART |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@property |
|
|
|
|
|
|
|
def is_pending(self): |
|
|
|
|
|
|
|
return self.status == BUYING_STATUS_PENDING |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@property |
|
|
|
|
|
|
|
def is_paid(self): |
|
|
|
|
|
|
|
return self.status == BUYING_STATUS_PAID |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __str__(self): |
|
|
|
|
|
|
|
return "{product_name}({product_amount}) - {price}".format(**{ |
|
|
|
|
|
|
|
'product_name': self.offer.product.name, |
|
|
|
|
|
|
|
'product_amount': self.amount, |
|
|
|
|
|
|
|
'price': self.total_price |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Meta: |
|
|
|
|
|
|
|
verbose_name = _('Покупка') |
|
|
|
|
|
|
|
verbose_name_plural = _('Покупки') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@receiver(post_save, sender=Order) |
|
|
|
@receiver(post_save, sender=Order) |
|
|
|
def product_in_order_post_save(sender, instance, created, **kwargs): |
|
|
|
def product_in_order_post_save(sender, instance, created, **kwargs): |
|
|
|
order = instance.order |
|
|
|
order = instance.order |
|
|
|
|