From 25153d6b7a527a7cf781cda748ac0f2b44d55dfc Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Sat, 20 Dec 2025 10:27:04 +0330 Subject: [PATCH] add price_in_dollor field and update related models and serializers --- backend/product/admin.py | 4 +- ...productvariant_price_in_dollor_and_more.py | 38 ++++++++++++++++ ...lter_dollormodel_defualt_price_and_more.py | 38 ++++++++++++++++ backend/product/models.py | 45 +++++++++++++------ backend/product/serializers.py | 2 +- 5 files changed, 110 insertions(+), 17 deletions(-) create mode 100644 backend/product/migrations/0063_productvariant_price_in_dollor_and_more.py create mode 100644 backend/product/migrations/0064_alter_dollormodel_defualt_price_and_more.py diff --git a/backend/product/admin.py b/backend/product/admin.py index f59c359..775fa56 100644 --- a/backend/product/admin.py +++ b/backend/product/admin.py @@ -214,10 +214,10 @@ class ProductVariantInLine(ProductVariantInlineAdminPermission, StackedInline): show_change_link = True tab = True min_num = 1 - readonly_fields = ['price', 'sell'] + readonly_fields = ['price', 'sell', 'price_in_dollor'] # inlines = [DetailModelInLine] autocomplete_fields = ['product_attributes', 'in_pack_items', 'images', 'details'] - fields = ['images', 'video','input_price', 'min_price', 'currency', 'price', 'discount','in_stock', 'color', 'product_attributes', 'in_pack_items', 'details', 'sell', 'slider_category', 'profit', 'special_discount_percent'] + fields = ['input_price', 'min_price', 'currency', 'price','price_in_dollor', 'profit', 'discount', 'special_discount_percent', 'in_stock', 'images', 'video', 'color', 'product_attributes', 'in_pack_items', 'details', 'sell', 'slider_category'] # search_fields = [''] def formfield_for_dbfield(self, db_field, request, **kwargs): diff --git a/backend/product/migrations/0063_productvariant_price_in_dollor_and_more.py b/backend/product/migrations/0063_productvariant_price_in_dollor_and_more.py new file mode 100644 index 0000000..f884303 --- /dev/null +++ b/backend/product/migrations/0063_productvariant_price_in_dollor_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 5.1.2 on 2025-12-20 05:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0062_alter_productmodel_image'), + ] + + operations = [ + migrations.AddField( + model_name='productvariant', + name='price_in_dollor', + field=models.FloatField(blank=True, help_text='قیمت محصول به دلار (محاسبه خودکار)', null=True, verbose_name='قیمت به دلار'), + ), + migrations.AlterField( + model_name='productvariant', + name='input_price', + field=models.PositiveBigIntegerField(default=0, verbose_name='قیمت ورودی'), + ), + migrations.AlterField( + model_name='productvariant', + name='min_price', + field=models.PositiveBigIntegerField(help_text='این قیمت برای کف قیمتی محصول در نظر گرفته میشود', verbose_name='قیمت کف'), + ), + migrations.AlterField( + model_name='productvariant', + name='price', + field=models.PositiveBigIntegerField(blank=True, null=True, verbose_name='قیمت محاسبه شده'), + ), + migrations.AlterField( + model_name='productvariant', + name='profit', + field=models.PositiveBigIntegerField(default=0, help_text='مقدار سود به ازای هر واحد به تومان', verbose_name='سود (تومان)'), + ), + ] diff --git a/backend/product/migrations/0064_alter_dollormodel_defualt_price_and_more.py b/backend/product/migrations/0064_alter_dollormodel_defualt_price_and_more.py new file mode 100644 index 0000000..7975653 --- /dev/null +++ b/backend/product/migrations/0064_alter_dollormodel_defualt_price_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 5.1.2 on 2025-12-20 06:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0063_productvariant_price_in_dollor_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='dollormodel', + name='defualt_price', + field=models.DecimalField(blank=True, decimal_places=2, default=80000.0, max_digits=10, null=True, verbose_name='قیمت دستی'), + ), + migrations.AlterField( + model_name='dollormodel', + name='price', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='قیمت دلار'), + ), + migrations.AlterField( + model_name='productvariant', + name='currency', + field=models.CharField(choices=[('dollor', 'با نوسان دلاری'), ('toman', 'بدون نوسان')], max_length=20, verbose_name='نوع نوسان'), + ), + migrations.AlterField( + model_name='productvariant', + name='input_price', + field=models.PositiveBigIntegerField(default=0, help_text='قیمت ورودی محصول', verbose_name='قیمت محصول'), + ), + migrations.AlterField( + model_name='productvariant', + name='price_in_dollor', + field=models.DecimalField(blank=True, decimal_places=5, help_text='قیمت محصول به دلار (محاسبه خودکار)', max_digits=12, null=True, verbose_name='قیمت به دلار'), + ), + ] diff --git a/backend/product/models.py b/backend/product/models.py index a2df4b8..b1a535f 100644 --- a/backend/product/models.py +++ b/backend/product/models.py @@ -6,6 +6,8 @@ import requests from django.utils.translation import gettext_lazy as _ from django.core.exceptions import ValidationError from home.models import ShowCaseSlider +from decimal import Decimal +from dirtyfields import DirtyFieldsMixin @@ -119,9 +121,9 @@ class SubCategoryModel(models.Model): class DollorModel(models.Model): - price = models.FloatField(null=True, blank=True, verbose_name='قیمت دلار') - defualt_price = models.FloatField( - null=True, blank=True, default=80000.0, verbose_name='قیمت دستی') + price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='قیمت دلار') + defualt_price = models.DecimalField( + max_digits=10, decimal_places=2, null=True, blank=True, default=80000.0, verbose_name='قیمت دستی') # these fields will avoid dublicate of this model unique = (('unique', 'unique'),) unique_filed = models.CharField( @@ -354,32 +356,35 @@ class DetailModel(models.Model): ] -class ProductVariant(models.Model): +class ProductVariant(DirtyFieldsMixin, models.Model): product = models.ForeignKey( ProductModel, on_delete=models.CASCADE, related_name='variants', verbose_name='محصول') product_attributes = models.ManyToManyField( AttributeValue, verbose_name='ویژگی‌ها', related_name='variant') in_stock = models.PositiveIntegerField( default=0, verbose_name='تعداد موجود') - price = models.PositiveIntegerField( + price = models.PositiveBigIntegerField( verbose_name='قیمت محاسبه شده', blank=True, null=True) - input_price = models.PositiveIntegerField( - default=0, verbose_name='قیمت ورودی') - min_price = models.PositiveIntegerField( + input_price = models.PositiveBigIntegerField( + default=0, verbose_name='قیمت محصول', help_text='قیمت ورودی محصول') + min_price = models.PositiveBigIntegerField( verbose_name='قیمت کف', help_text='این قیمت برای کف قیمتی محصول در نظر گرفته میشود') - profit = models.BigIntegerField( + profit = models.PositiveBigIntegerField( default=0, verbose_name='سود (تومان)', help_text='مقدار سود به ازای هر واحد به تومان') special_discount_percent = models.SmallIntegerField( default=0, verbose_name='درصد تخفیف ویژه', help_text='درصدی که از سود برای محاسبه تخفیف ویژه استفاده می‌شود') currency_type = ( - ('dollor', 'دلار'), - ('toman', 'تومان'), + ('dollor', 'با نوسان دلاری'), + ('toman', 'بدون نوسان'), ) in_pack_items = models.ManyToManyField( InPackItems, blank=True, verbose_name='ایتم های داخل پک') sell = models.IntegerField(default=0, verbose_name='فروش') currency = models.CharField( - verbose_name='نوع ارز', max_length=20, choices=currency_type) + verbose_name='نوع نوسان', max_length=20, choices=currency_type) + price_in_dollor = models.DecimalField( + max_digits=12, decimal_places=5, blank=True, null=True, + verbose_name='قیمت به دلار', help_text='قیمت محصول به دلار (محاسبه خودکار)') discount = models.SmallIntegerField(default=0, verbose_name='تخفیف') color = models.CharField( verbose_name='رنگ', max_length=7, blank=True, null=True) @@ -451,12 +456,24 @@ class ProductVariant(models.Model): if self.currency == 'toman': toman_price = self.input_price + # Clear dollar price when currency is toman + self.price_in_dollor = None elif self.currency == 'dollor': - toman_price = self.input_price * dollor_price + # Only recalculate dollar price if input_price has changed + if self.is_dirty(check_relationship=False) and 'input_price' in self.get_dirty_fields(check_relationship=False): + self.price_in_dollor = Decimal(str(self.input_price)) / Decimal(str(dollor_price)) + + # Always recalculate Toman price using stored dollar price and current rate + if self.price_in_dollor: + toman_price = self.price_in_dollor * Decimal(str(dollor_price)) + else: + # Fallback if price_in_dollor is not set yet + toman_price = self.input_price else: toman_price = self.input_price + self.price_in_dollor = None - self.price = max(toman_price, self.min_price) + self.price = max(int(toman_price), self.min_price) def save(self, *args, **kwargs): self.set_or_update_price() diff --git a/backend/product/serializers.py b/backend/product/serializers.py index 295a219..240f4e0 100644 --- a/backend/product/serializers.py +++ b/backend/product/serializers.py @@ -69,7 +69,7 @@ class ProductVariantSerialzier(serializers.ModelSerializer): class Meta: model = ProductVariant - exclude = ('min_price', 'sell', 'currency', 'product', 'input_price') + exclude = ('min_price', 'sell', 'currency', 'product', 'input_price', 'price_in_dollor') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs)