+ PriceSnapshot
* Category commission inside a commission_rub field
This commit is contained in:
parent
ce59792419
commit
8c0f5ac6fd
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Generated by Django 4.2.2 on 2023-10-04 02:53
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('store', '0042_oldchecklist_checklist_split_payment_proof_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='PriceSnapshot',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('yuan_rate', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='Курс CNY/RUB')),
|
||||||
|
('delivery_price_CN', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='Цена доставки по Китаю')),
|
||||||
|
('delivery_price_CN_RU', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='Цена доставки Китай-РФ')),
|
||||||
|
('commission_rub', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='Комиссия, руб')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='checklist',
|
||||||
|
name='price_snapshot',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='checklist', to='store.pricesnapshot', verbose_name='Сохраненные цены'),
|
||||||
|
),
|
||||||
|
]
|
||||||
116
store/models.py
116
store/models.py
|
|
@ -228,17 +228,23 @@ def generate_checklist_id():
|
||||||
|
|
||||||
class ChecklistQuerySet(models.QuerySet):
|
class ChecklistQuerySet(models.QuerySet):
|
||||||
def with_base_related(self):
|
def with_base_related(self):
|
||||||
return self.select_related('manager', 'category', 'payment_method', 'promocode')\
|
return self.select_related('manager', 'category', 'payment_method', 'promocode', 'price_snapshot')\
|
||||||
.prefetch_related(Prefetch('images', to_attr='_images'))
|
.prefetch_related(Prefetch('images', to_attr='_images'))
|
||||||
|
|
||||||
def default_ordering(self):
|
def default_ordering(self):
|
||||||
return self.order_by(F('status_updated_at').desc(nulls_last=True))
|
return self.order_by(F('status_updated_at').desc(nulls_last=True))
|
||||||
|
|
||||||
def annotate_price_rub(self):
|
def annotate_price_rub(self):
|
||||||
|
# FIXME: implement price_rub in DB query
|
||||||
|
return self
|
||||||
|
|
||||||
yuan_rate = GlobalSettings.load().yuan_rate
|
yuan_rate = GlobalSettings.load().yuan_rate
|
||||||
return self.annotate(_price_rub=F('price_yuan') * yuan_rate)
|
return self.annotate(_price_rub=F('price_yuan') * yuan_rate)
|
||||||
|
|
||||||
def annotate_commission_rub(self):
|
def annotate_commission_rub(self):
|
||||||
|
# FIXME: implement category commission in DB query
|
||||||
|
return self
|
||||||
|
|
||||||
commission = GlobalSettings.load().commission_rub
|
commission = GlobalSettings.load().commission_rub
|
||||||
return self.annotate(_commission_rub=Case(
|
return self.annotate(_commission_rub=Case(
|
||||||
When(GreaterThan(F("_price_rub"), 150_000), then=F("_price_rub") * settings.COMMISSION_OVER_150K),
|
When(GreaterThan(F("_price_rub"), 150_000), then=F("_price_rub") * settings.COMMISSION_OVER_150K),
|
||||||
|
|
@ -247,6 +253,13 @@ class ChecklistQuerySet(models.QuerySet):
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
|
class PriceSnapshot(models.Model):
|
||||||
|
yuan_rate = models.DecimalField('Курс CNY/RUB', max_digits=10, decimal_places=2, default=0)
|
||||||
|
delivery_price_CN = models.DecimalField('Цена доставки по Китаю', max_digits=10, decimal_places=2, default=0)
|
||||||
|
delivery_price_CN_RU = models.DecimalField('Цена доставки Китай-РФ', max_digits=10, decimal_places=2, default=0)
|
||||||
|
commission_rub = models.DecimalField('Комиссия, руб', max_digits=10, decimal_places=2, default=0)
|
||||||
|
|
||||||
|
|
||||||
@cleanup.select
|
@cleanup.select
|
||||||
class Checklist(models.Model):
|
class Checklist(models.Model):
|
||||||
# Statuses
|
# Statuses
|
||||||
|
|
@ -350,6 +363,10 @@ class Checklist(models.Model):
|
||||||
cdek_tracking = models.CharField('Трек-номер СДЭК', max_length=100, null=True, blank=True)
|
cdek_tracking = models.CharField('Трек-номер СДЭК', max_length=100, null=True, blank=True)
|
||||||
cdek_barcode_pdf = models.FileField('Штрих-код СДЭК в PDF', upload_to='docs', null=True, blank=True)
|
cdek_barcode_pdf = models.FileField('Штрих-код СДЭК в PDF', upload_to='docs', null=True, blank=True)
|
||||||
|
|
||||||
|
price_snapshot = models.ForeignKey('PriceSnapshot', verbose_name='Сохраненные цены',
|
||||||
|
related_name='checklist',
|
||||||
|
on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
|
|
||||||
objects = ChecklistQuerySet.as_manager()
|
objects = ChecklistQuerySet.as_manager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -368,11 +385,18 @@ class Checklist(models.Model):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def price_rub(self) -> int:
|
def price_rub(self) -> int:
|
||||||
# Prefer annotated field
|
# FIXME: implement price_rub in DB query
|
||||||
if hasattr(self, '_price_rub'):
|
# # Prefer annotated field for calculation
|
||||||
return self._price_rub
|
# if hasattr(self, '_price_rub'):
|
||||||
|
# return self._price_rub
|
||||||
|
|
||||||
return math.ceil(GlobalSettings.load().yuan_rate * self.price_yuan)
|
# Get saved prices
|
||||||
|
if self.price_snapshot_id:
|
||||||
|
yuan_rate = self.price_snapshot.yuan_rate
|
||||||
|
else:
|
||||||
|
yuan_rate = GlobalSettings.load().yuan_rate
|
||||||
|
|
||||||
|
return math.ceil(yuan_rate * self.price_yuan)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def full_price(self) -> int:
|
def full_price(self) -> int:
|
||||||
|
|
@ -392,32 +416,58 @@ class Checklist(models.Model):
|
||||||
no_comission = promocode.no_comission
|
no_comission = promocode.no_comission
|
||||||
|
|
||||||
if not free_delivery:
|
if not free_delivery:
|
||||||
price += GlobalSettings.load().delivery_price_CN + self.delivery_price_CN_RU
|
price += self.delivery_price_CN + self.delivery_price_CN_RU
|
||||||
|
|
||||||
if not no_comission:
|
if not no_comission:
|
||||||
price += self.commission_rub
|
price += self.commission_rub
|
||||||
|
|
||||||
# Add commission of bottom-most category
|
|
||||||
if self.category:
|
|
||||||
category = self.category.get_ancestors(ascending=True, include_self=True).first()
|
|
||||||
category_commission = getattr(category, 'commission', 0)
|
|
||||||
price += category_commission * self.price_rub / 100
|
|
||||||
|
|
||||||
return max(0, math.ceil(price))
|
return max(0, math.ceil(price))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def yuan_rate(self) -> Decimal:
|
||||||
|
# Get saved value if exists
|
||||||
|
if self.price_snapshot_id:
|
||||||
|
return self.price_snapshot.yuan_rate
|
||||||
|
else:
|
||||||
|
return GlobalSettings.load().yuan_rate
|
||||||
|
|
||||||
|
@property
|
||||||
|
def delivery_price_CN(self) -> Decimal:
|
||||||
|
# Get saved value if exists
|
||||||
|
if self.price_snapshot_id:
|
||||||
|
return self.price_snapshot.delivery_price_CN
|
||||||
|
else:
|
||||||
|
return GlobalSettings.load().delivery_price_CN
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def delivery_price_CN_RU(self) -> Decimal:
|
def delivery_price_CN_RU(self) -> Decimal:
|
||||||
return getattr(self.category, 'delivery_price_CN_RU', Decimal(0))
|
# Get saved value if exists
|
||||||
|
if self.price_snapshot_id:
|
||||||
|
return self.price_snapshot.delivery_price_CN_RU
|
||||||
|
else:
|
||||||
|
return getattr(self.category, 'delivery_price_CN_RU', Decimal(0))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def commission_rub(self) -> Decimal:
|
def commission_rub(self) -> Decimal:
|
||||||
# Prefer annotated field
|
# FIXME: implement category commission in DB query
|
||||||
if hasattr(self, '_commission_rub'):
|
# # Prefer annotated field
|
||||||
return self._commission_rub
|
# if hasattr(self, '_commission_rub'):
|
||||||
|
# return self._commission_rub
|
||||||
|
|
||||||
return (self.price_rub * Decimal(settings.COMMISSION_OVER_150K)
|
# Prefer saved value
|
||||||
if self.price_rub > 150_000
|
if self.price_snapshot_id:
|
||||||
else GlobalSettings.load().commission_rub)
|
return self.price_snapshot.commission_rub
|
||||||
|
|
||||||
|
commission = (self.price_rub * Decimal(settings.COMMISSION_OVER_150K)
|
||||||
|
if self.price_rub > 150_000
|
||||||
|
else GlobalSettings.load().commission_rub)
|
||||||
|
|
||||||
|
# Add commission of bottom-most category
|
||||||
|
if self.category_id:
|
||||||
|
category_commission = getattr(self.category, 'commission', 0)
|
||||||
|
commission += category_commission * self.price_rub / 100
|
||||||
|
|
||||||
|
return commission
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def preview_image(self):
|
def preview_image(self):
|
||||||
|
|
@ -467,9 +517,27 @@ class Checklist(models.Model):
|
||||||
|
|
||||||
self.images.add(image_obj)
|
self.images.add(image_obj)
|
||||||
|
|
||||||
|
def save_prices(self):
|
||||||
|
# Temporarily remove snapshot from object
|
||||||
|
self.price_snapshot = None
|
||||||
|
|
||||||
|
snapshot, _ = PriceSnapshot.objects.get_or_create(
|
||||||
|
checklist__id=self.price_snapshot_id,
|
||||||
|
defaults={
|
||||||
|
'yuan_rate': self.yuan_rate,
|
||||||
|
'delivery_price_CN': self.delivery_price_CN,
|
||||||
|
'delivery_price_CN_RU': self.delivery_price_CN_RU,
|
||||||
|
'commission_rub': self.commission_rub,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Restore snapshot
|
||||||
|
self.price_snapshot = snapshot
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if self.id:
|
if self.id:
|
||||||
old_obj = Checklist.objects.filter(id=self.id).first()
|
old_obj = Checklist.objects.filter(id=self.id).first()
|
||||||
|
|
||||||
# If status was updated, update status_updated_at field
|
# If status was updated, update status_updated_at field
|
||||||
if old_obj and self.status != old_obj.status:
|
if old_obj and self.status != old_obj.status:
|
||||||
self.status_updated_at = timezone.now()
|
self.status_updated_at = timezone.now()
|
||||||
|
|
@ -485,5 +553,15 @@ class Checklist(models.Model):
|
||||||
if not self.preview_image:
|
if not self.preview_image:
|
||||||
self.generate_preview()
|
self.generate_preview()
|
||||||
|
|
||||||
|
# Save price details to snapshot
|
||||||
|
if self.price_snapshot_id:
|
||||||
|
# Status updated from other statuses back to DRAFT
|
||||||
|
if self.status == Checklist.Status.DRAFT:
|
||||||
|
self.price_snapshot.delete()
|
||||||
|
self.price_snapshot = None
|
||||||
|
|
||||||
|
elif self.status != Checklist.Status.DRAFT:
|
||||||
|
self.save_prices()
|
||||||
|
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -75,16 +75,16 @@ class ChecklistSerializer(serializers.ModelSerializer):
|
||||||
promo = serializers.SlugRelatedField(source='promocode', slug_field='name',
|
promo = serializers.SlugRelatedField(source='promocode', slug_field='name',
|
||||||
queryset=Promocode.objects.active(), required=False, allow_null=True)
|
queryset=Promocode.objects.active(), required=False, allow_null=True)
|
||||||
|
|
||||||
currency = serializers.SerializerMethodField('get_yuan_rate')
|
currency = serializers.DecimalField(source='yuan_rate', read_only=True, max_digits=10, decimal_places=2)
|
||||||
curencycurency2 = serializers.DecimalField(source='price_yuan', required=False, max_digits=10, decimal_places=2)
|
curencycurency2 = serializers.DecimalField(source='price_yuan', required=False, max_digits=10, decimal_places=2)
|
||||||
currency3 = serializers.IntegerField(source='price_rub', read_only=True)
|
currency3 = serializers.IntegerField(source='price_rub', read_only=True)
|
||||||
chinadelivery = serializers.SerializerMethodField('get_delivery_price_CN', read_only=True)
|
chinadelivery = serializers.DecimalField(source='delivery_price_CN', read_only=True, max_digits=10, decimal_places=2)
|
||||||
chinadelivery2 = serializers.DecimalField(source='delivery_price_CN_RU', read_only=True, max_digits=10,
|
chinadelivery2 = serializers.DecimalField(source='delivery_price_CN_RU', read_only=True,
|
||||||
decimal_places=2)
|
max_digits=10, decimal_places=2)
|
||||||
fullprice = serializers.IntegerField(source='full_price', read_only=True)
|
fullprice = serializers.IntegerField(source='full_price', read_only=True)
|
||||||
realprice = serializers.DecimalField(source='real_price', required=False, allow_null=True, max_digits=10,
|
realprice = serializers.DecimalField(source='real_price', required=False, allow_null=True, max_digits=10,
|
||||||
decimal_places=2)
|
decimal_places=2)
|
||||||
commission = serializers.SerializerMethodField('get_commission', read_only=True)
|
commission = serializers.DecimalField(source='commission_rub', read_only=True, max_digits=10, decimal_places=2)
|
||||||
|
|
||||||
buyername = serializers.CharField(source='buyer_name', required=False, allow_null=True)
|
buyername = serializers.CharField(source='buyer_name', required=False, allow_null=True)
|
||||||
buyerphone = serializers.CharField(source='buyer_phone', required=False, allow_null=True)
|
buyerphone = serializers.CharField(source='buyer_phone', required=False, allow_null=True)
|
||||||
|
|
@ -151,22 +151,10 @@ class ChecklistSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_yuan_rate(obj: Checklist):
|
|
||||||
return GlobalSettings.load().yuan_rate
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_image(obj: Checklist):
|
def get_image(obj: Checklist):
|
||||||
return obj.images.all()
|
return obj.images.all()
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_delivery_price_CN(obj: Checklist):
|
|
||||||
return GlobalSettings.load().delivery_price_CN
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_commission(obj: Checklist):
|
|
||||||
return GlobalSettings.load().commission_rub
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Checklist
|
model = Checklist
|
||||||
fields = ('id', 'status', 'managerid', 'link',
|
fields = ('id', 'status', 'managerid', 'link',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user