cron job added if it wont work fck this
This commit is contained in:
@@ -31,5 +31,5 @@ jobs:
|
||||
script: |
|
||||
cd /root/hshop/
|
||||
docker compose down
|
||||
docker compose build
|
||||
docker compose build --no-cache
|
||||
docker compose up -d
|
||||
@@ -30,7 +30,7 @@ EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD")
|
||||
DEFAULT_FROM_EMAIL = os.getenv("SECRET_KEY")
|
||||
|
||||
# Security and Debugging
|
||||
SECRET_KEY = os.getenv("SECRET_KEY")
|
||||
SECRET_KEY = os.getenv("SECRET_KEY") or 'this is just for cron job dont judge me'
|
||||
DEBUG = True
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
||||
|
||||
@@ -65,6 +65,7 @@ INSTALLED_APPS = [
|
||||
"rest_framework.authtoken",
|
||||
"import_export",
|
||||
"django_jalali",
|
||||
'django_crontab',
|
||||
# Custom Apps
|
||||
"product",
|
||||
"account",
|
||||
@@ -233,3 +234,12 @@ AWS_S3_OBJECT_PARAMETERS = {
|
||||
'ACL': 'public-read',
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# django CRONJOBS
|
||||
# ==============================================================================
|
||||
|
||||
CRONJOBS = [
|
||||
('* * * * *', 'product.cron.update_product_prices', f'>> {BASE_DIR}/logfile.log 2>&1'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -13,5 +13,6 @@ COPY . /app/
|
||||
|
||||
CMD ["sh", "-c", "python manage.py makemigrations && \
|
||||
python manage.py migrate && \
|
||||
python manage.py crontab add && \
|
||||
python manage.py collectstatic --no-input && \
|
||||
gunicorn core.wsgi:application --bind 0.0.0.0:8000 --workers 3"]
|
||||
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.1.2 on 2025-03-08 18:54
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('order', '0010_alter_orderitemmodel_quantity'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='orderitemmodel',
|
||||
name='price',
|
||||
field=models.PositiveIntegerField(default=0, verbose_name='قیمت'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
@@ -97,13 +97,14 @@ class OrderModel(models.Model):
|
||||
class OrderItemModel(models.Model):
|
||||
order = models.ForeignKey(OrderModel, on_delete=models.CASCADE, related_name='items', verbose_name='سفارش')
|
||||
quantity = models.PositiveSmallIntegerField(verbose_name="تعداد")
|
||||
price = models.PositiveIntegerField(verbose_name='قیمت')
|
||||
product = models.ForeignKey(ProductVariant, on_delete=models.PROTECT, verbose_name="محصول")
|
||||
class Meta:
|
||||
verbose_name = 'ایتم سبد خرید'
|
||||
verbose_name_plural = 'ایتم های سبد خرید'
|
||||
|
||||
def total(self):
|
||||
return self.quantity * self.product.get_toman_price()
|
||||
return self.quantity * self.product.price
|
||||
|
||||
def total_with_discount(self):
|
||||
return self.quantity * self.product.get_toman_price_after_discount()
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
from rest_framework import serializers
|
||||
from .models import OrderItemModel, OrderModel
|
||||
|
||||
from product.serializers import ProductVariantSerialzier
|
||||
|
||||
class OrderItemSerailzier(serializers.ModelSerializer):
|
||||
product = serializers.SerializerMethodField()
|
||||
class Meta:
|
||||
model = OrderItemModel
|
||||
fields = "__all__"
|
||||
read_only_fields = ('order', 'product')
|
||||
def get_product(self, obj):
|
||||
return ProductVariantSerialzier(instance=obj.product, context={'request': self.context.get('request')}).data
|
||||
|
||||
class CartSerializer(serializers.ModelSerializer):
|
||||
items = OrderItemSerailzier(many=True)
|
||||
|
||||
@@ -9,7 +9,7 @@ from django.contrib.postgres.fields import ArrayField
|
||||
from unfold.widgets import UnfoldAdminColorInputWidget
|
||||
from unfold.decorators import action, display
|
||||
from utils.admin import ModelAdmin
|
||||
|
||||
from django.shortcuts import redirect
|
||||
|
||||
|
||||
|
||||
@@ -140,8 +140,10 @@ class ProductVariantInLine(StackedInline):
|
||||
show_change_link = True
|
||||
tab = True
|
||||
min_num = 1
|
||||
readonly_fields = ['price']
|
||||
# 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']
|
||||
# search_fields = ['']
|
||||
|
||||
|
||||
@@ -156,6 +158,7 @@ class ProductVariantAdmin(ModelAdmin, ImportExportModelAdmin):
|
||||
export_form_class = ExportForm
|
||||
autocomplete_fields = ['product_attributes', 'images', 'in_pack_items', 'details']
|
||||
warn_unsaved_form = True
|
||||
readonly_fields = ['price']
|
||||
# inlines = [DetailModelInLine]
|
||||
|
||||
@admin.register(ProductModel)
|
||||
@@ -169,6 +172,7 @@ class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin):
|
||||
autocomplete_fields = ['related_products', ]
|
||||
# compressed_fields = True
|
||||
warn_unsaved_form = True
|
||||
actions_list = ['redirect_to_learn', 'update_products_price']
|
||||
list_display = ['display_image', 'display_price', 'view', 'show', 'rating', 'category', ]
|
||||
fieldsets = (
|
||||
('فیلد های اصلی', {'fields': ('name', 'description', 'category', 'related_products', 'show',), "classes": ["tab"],}),
|
||||
@@ -188,7 +192,7 @@ class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin):
|
||||
|
||||
def display_price(self, obj):
|
||||
if obj.variants.all().first():
|
||||
return obj.variants.all().first().get_toman_price()
|
||||
return obj.variants.all().first().price
|
||||
display_price.short_description = 'قیمت تومانی'
|
||||
|
||||
@display(description='محصول', header=True)
|
||||
@@ -209,6 +213,15 @@ class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin):
|
||||
# "squared": True,
|
||||
},
|
||||
]
|
||||
|
||||
@action(description=f"اپدیت قیمت ها")
|
||||
def update_products_price(self, request):
|
||||
print('from the button')
|
||||
ProductVariant.update_all_prices()
|
||||
messages.success(request, f"قیمت {ProductVariant.objects.all().count()} تنوع محصول اپدیت شد")
|
||||
return redirect("admin:product_productmodel_changelist")
|
||||
|
||||
|
||||
# @display(
|
||||
# description=("نمایش در صفحه ی اصلی"),
|
||||
# label={
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from product.models import ProductVariant
|
||||
|
||||
def update_product_prices():
|
||||
print('calling the update product prices from cron')
|
||||
ProductVariant.update_all_prices()
|
||||
@@ -0,0 +1,37 @@
|
||||
# Generated by Django 5.1.2 on 2025-03-08 18:54
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('product', '0033_alter_productvariant_details'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='productvariant',
|
||||
name='max_price',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='productvariant',
|
||||
name='input_price',
|
||||
field=models.PositiveIntegerField(default=0, verbose_name='قیمت ورودی'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='productvariant',
|
||||
name='color',
|
||||
field=models.CharField(blank=True, max_length=7, null=True, verbose_name='رنگ'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='productvariant',
|
||||
name='details',
|
||||
field=models.ManyToManyField(related_name='product', to='product.productdetailmodel', verbose_name='جزییات محصول'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='productvariant',
|
||||
name='price',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='قیمت محاسبه شده'),
|
||||
),
|
||||
]
|
||||
+36
-14
@@ -215,14 +215,13 @@ class ProductDetailModel(models.Model):
|
||||
# def __str__(self):
|
||||
# return f'جزيیات محصول {self.product}'
|
||||
|
||||
|
||||
class ProductVariant(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(default=0, verbose_name='قیمت')
|
||||
price = models.PositiveIntegerField(verbose_name='قیمت محاسبه شده', blank=True, null=True)
|
||||
input_price = models.PositiveIntegerField(default=0, verbose_name='قیمت ورودی')
|
||||
min_price = models.PositiveIntegerField(verbose_name='قیمت کف', help_text='این قیمت برای کف قیمتی محصول در نظر گرفته میشود')
|
||||
max_price = models.PositiveIntegerField(verbose_name='قیمت سقف', help_text='این قیمت برای سقف قیمتی محصول در نظر گرفته میشود')
|
||||
currency_type = (
|
||||
('dollor', 'دلار'),
|
||||
('toman', 'تومان'),
|
||||
@@ -232,33 +231,56 @@ class ProductVariant(models.Model):
|
||||
sell = models.IntegerField(default=0, verbose_name='فروش')
|
||||
currency = models.CharField(verbose_name='نوع ارز', max_length=20, choices=currency_type)
|
||||
discount = models.SmallIntegerField(default=0, verbose_name='تخفیف')
|
||||
color = models.CharField(verbose_name='رنک', max_length=7, blank=True, null=True)
|
||||
color = models.CharField(verbose_name='رنگ', max_length=7, blank=True, null=True)
|
||||
images = models.ManyToManyField(ProductImageModel, verbose_name='عکس ها')
|
||||
video = models.FileField(upload_to='product_videos/', blank=True, null=True, verbose_name='ویدیو')
|
||||
details = models.ManyToManyField(ProductDetailModel, verbose_name='جزيیات محصول', related_name='product')
|
||||
details = models.ManyToManyField(ProductDetailModel, verbose_name='جزییات محصول', related_name='product')
|
||||
class Meta:
|
||||
verbose_name = 'تنوع محصول'
|
||||
verbose_name_plural = 'تنوعهای محصول'
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.product.name} - {', '.join(str(attr) for attr in self.product_attributes.all())}"
|
||||
|
||||
def get_toman_price(self, dollor_price=None):
|
||||
|
||||
def set_or_update_price(self, dollor_price=None):
|
||||
if not dollor_price:
|
||||
dollor_object, _ = DollorModel.objects.get_or_create(unique_filed='unique')
|
||||
dollor_price = dollor_object.price
|
||||
|
||||
dollar_to_dirham = 0.27
|
||||
if dollor_price is None:
|
||||
raise ValidationError({"dollor_price": "The 'dollor_price' must be provided in the context for dollar pricing."})
|
||||
|
||||
dollar_to_dirham = 0.27
|
||||
|
||||
if self.currency == 'toman':
|
||||
toman_price = self.price
|
||||
toman_price = self.input_price
|
||||
elif self.currency == 'dollor':
|
||||
toman_price = self.price * dollor_price
|
||||
toman_price = self.input_price * dollor_price
|
||||
elif self.currency == 'derham':
|
||||
toman_price = self.price * dollor_price * dollar_to_dirham
|
||||
toman_price = toman_price if toman_price > self.min_price else self.min_price
|
||||
return toman_price
|
||||
toman_price = self.input_price * dollor_price * dollar_to_dirham
|
||||
else:
|
||||
toman_price = self.input_price
|
||||
|
||||
self.price = max(toman_price, self.min_price)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.set_or_update_price()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def get_toman_price_after_discount(self):
|
||||
return self.get_toman_price() * ((100 - self.discount) / 100)
|
||||
return self.price * ((100 - self.discount) / 100)
|
||||
|
||||
@classmethod
|
||||
def update_all_prices(cls):
|
||||
print('calling the update all prices ')
|
||||
dollor_object, _ = DollorModel.objects.get_or_create(unique_filed='unique')
|
||||
print(dollor_object.price)
|
||||
dollor_object.update_price()
|
||||
dollor_object.save()
|
||||
dollor_price = dollor_object.price
|
||||
print(dollor_object.price)
|
||||
print('classmethod dollor price update ')
|
||||
products = cls.objects.all()
|
||||
for product in products:
|
||||
product.set_or_update_price(dollor_price=dollor_price)
|
||||
product.save()
|
||||
@@ -46,18 +46,13 @@ class ProductImageSerailizer(serializers.ModelSerializer):
|
||||
|
||||
class ProductVariantSerialzier(serializers.ModelSerializer):
|
||||
product_attributes = AttributeValueSerialzier(many=True)
|
||||
price = serializers.SerializerMethodField()
|
||||
in_pack_items = InPackItemsSerialzier(many=True)
|
||||
images = ProductImageSerailizer(many=True)
|
||||
details = ProductDetailSerializer(many=True, read_only=True)
|
||||
class Meta:
|
||||
model = ProductVariant
|
||||
exclude = ('min_price', 'max_price','sell', 'currency', 'product')
|
||||
exclude = ('min_price', 'sell', 'currency', 'product', 'input_price')
|
||||
|
||||
def get_price(self, obj):
|
||||
dollor_price = self.context.get('dollor_price')
|
||||
toman_price = obj.get_toman_price(dollor_price=dollor_price)
|
||||
return "{:,.0f} تومان".format(toman_price)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -25,6 +25,7 @@ django-cleanup==8.1.0
|
||||
django-colorfield==0.11.0
|
||||
django-cors-headers==4.4.0
|
||||
django-cron==0.6.0
|
||||
django-crontab==0.7.1
|
||||
django-dbbackup==4.2.1
|
||||
django-dirtyfields==1.9.3
|
||||
django-filter==24.3
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ services:
|
||||
[
|
||||
"sh",
|
||||
"-c",
|
||||
"python manage.py migrate && python manage.py collectstatic --no-input && gunicorn core.wsgi:application --bind 0.0.0.0:8000 --workers 3",
|
||||
"python manage.py migrate && python manage.py collectstatic --no-input && python manage.py crontab add && gunicorn core.wsgi:application --bind 0.0.0.0:8000 --workers 3",
|
||||
]
|
||||
networks:
|
||||
- default
|
||||
|
||||
Reference in New Issue
Block a user