cron job added if it wont work fck this

This commit is contained in:
Parsa Nazer
2025-03-09 01:13:21 +03:30
parent 3a3706f1f9
commit 7c0c4329b1
13 changed files with 134 additions and 27 deletions
+1 -1
View File
@@ -31,5 +31,5 @@ jobs:
script: | script: |
cd /root/hshop/ cd /root/hshop/
docker compose down docker compose down
docker compose build docker compose build --no-cache
docker compose up -d docker compose up -d
+11 -1
View File
@@ -30,7 +30,7 @@ EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD")
DEFAULT_FROM_EMAIL = os.getenv("SECRET_KEY") DEFAULT_FROM_EMAIL = os.getenv("SECRET_KEY")
# Security and Debugging # 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 DEBUG = True
BASE_DIR = Path(__file__).resolve().parent.parent.parent BASE_DIR = Path(__file__).resolve().parent.parent.parent
@@ -65,6 +65,7 @@ INSTALLED_APPS = [
"rest_framework.authtoken", "rest_framework.authtoken",
"import_export", "import_export",
"django_jalali", "django_jalali",
'django_crontab',
# Custom Apps # Custom Apps
"product", "product",
"account", "account",
@@ -233,3 +234,12 @@ AWS_S3_OBJECT_PARAMETERS = {
'ACL': 'public-read', 'ACL': 'public-read',
} }
# ==============================================================================
# django CRONJOBS
# ==============================================================================
CRONJOBS = [
('* * * * *', 'product.cron.update_product_prices', f'>> {BASE_DIR}/logfile.log 2>&1'),
]
+1
View File
@@ -13,5 +13,6 @@ COPY . /app/
CMD ["sh", "-c", "python manage.py makemigrations && \ CMD ["sh", "-c", "python manage.py makemigrations && \
python manage.py migrate && \ python manage.py migrate && \
python manage.py crontab add && \
python manage.py collectstatic --no-input && \ python manage.py collectstatic --no-input && \
gunicorn core.wsgi:application --bind 0.0.0.0:8000 --workers 3"] 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,
),
]
+2 -1
View File
@@ -97,13 +97,14 @@ class OrderModel(models.Model):
class OrderItemModel(models.Model): class OrderItemModel(models.Model):
order = models.ForeignKey(OrderModel, on_delete=models.CASCADE, related_name='items', verbose_name='سفارش') order = models.ForeignKey(OrderModel, on_delete=models.CASCADE, related_name='items', verbose_name='سفارش')
quantity = models.PositiveSmallIntegerField(verbose_name="تعداد") quantity = models.PositiveSmallIntegerField(verbose_name="تعداد")
price = models.PositiveIntegerField(verbose_name='قیمت')
product = models.ForeignKey(ProductVariant, on_delete=models.PROTECT, verbose_name="محصول") product = models.ForeignKey(ProductVariant, on_delete=models.PROTECT, verbose_name="محصول")
class Meta: class Meta:
verbose_name = 'ایتم سبد خرید' verbose_name = 'ایتم سبد خرید'
verbose_name_plural = 'ایتم های سبد خرید' verbose_name_plural = 'ایتم های سبد خرید'
def total(self): def total(self):
return self.quantity * self.product.get_toman_price() return self.quantity * self.product.price
def total_with_discount(self): def total_with_discount(self):
return self.quantity * self.product.get_toman_price_after_discount() return self.quantity * self.product.get_toman_price_after_discount()
+4 -1
View File
@@ -1,12 +1,15 @@
from rest_framework import serializers from rest_framework import serializers
from .models import OrderItemModel, OrderModel from .models import OrderItemModel, OrderModel
from product.serializers import ProductVariantSerialzier
class OrderItemSerailzier(serializers.ModelSerializer): class OrderItemSerailzier(serializers.ModelSerializer):
product = serializers.SerializerMethodField()
class Meta: class Meta:
model = OrderItemModel model = OrderItemModel
fields = "__all__" fields = "__all__"
read_only_fields = ('order', 'product') 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): class CartSerializer(serializers.ModelSerializer):
items = OrderItemSerailzier(many=True) items = OrderItemSerailzier(many=True)
+15 -2
View File
@@ -9,7 +9,7 @@ from django.contrib.postgres.fields import ArrayField
from unfold.widgets import UnfoldAdminColorInputWidget from unfold.widgets import UnfoldAdminColorInputWidget
from unfold.decorators import action, display from unfold.decorators import action, display
from utils.admin import ModelAdmin from utils.admin import ModelAdmin
from django.shortcuts import redirect
@@ -140,8 +140,10 @@ class ProductVariantInLine(StackedInline):
show_change_link = True show_change_link = True
tab = True tab = True
min_num = 1 min_num = 1
readonly_fields = ['price']
# inlines = [DetailModelInLine] # inlines = [DetailModelInLine]
autocomplete_fields = ['product_attributes', 'in_pack_items', 'images', 'details'] 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 = [''] # search_fields = ['']
@@ -156,6 +158,7 @@ class ProductVariantAdmin(ModelAdmin, ImportExportModelAdmin):
export_form_class = ExportForm export_form_class = ExportForm
autocomplete_fields = ['product_attributes', 'images', 'in_pack_items', 'details'] autocomplete_fields = ['product_attributes', 'images', 'in_pack_items', 'details']
warn_unsaved_form = True warn_unsaved_form = True
readonly_fields = ['price']
# inlines = [DetailModelInLine] # inlines = [DetailModelInLine]
@admin.register(ProductModel) @admin.register(ProductModel)
@@ -169,6 +172,7 @@ class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin):
autocomplete_fields = ['related_products', ] autocomplete_fields = ['related_products', ]
# compressed_fields = True # compressed_fields = True
warn_unsaved_form = True warn_unsaved_form = True
actions_list = ['redirect_to_learn', 'update_products_price']
list_display = ['display_image', 'display_price', 'view', 'show', 'rating', 'category', ] list_display = ['display_image', 'display_price', 'view', 'show', 'rating', 'category', ]
fieldsets = ( fieldsets = (
('فیلد های اصلی', {'fields': ('name', 'description', 'category', 'related_products', 'show',), "classes": ["tab"],}), ('فیلد های اصلی', {'fields': ('name', 'description', 'category', 'related_products', 'show',), "classes": ["tab"],}),
@@ -188,7 +192,7 @@ class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin):
def display_price(self, obj): def display_price(self, obj):
if obj.variants.all().first(): if obj.variants.all().first():
return obj.variants.all().first().get_toman_price() return obj.variants.all().first().price
display_price.short_description = 'قیمت تومانی' display_price.short_description = 'قیمت تومانی'
@display(description='محصول', header=True) @display(description='محصول', header=True)
@@ -209,6 +213,15 @@ class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin):
# "squared": True, # "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( # @display(
# description=("نمایش در صفحه ی اصلی"), # description=("نمایش در صفحه ی اصلی"),
# label={ # label={
+5
View File
@@ -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
View File
@@ -215,14 +215,13 @@ class ProductDetailModel(models.Model):
# def __str__(self): # def __str__(self):
# return f'جزيیات محصول {self.product}' # return f'جزيیات محصول {self.product}'
class ProductVariant(models.Model): class ProductVariant(models.Model):
product = models.ForeignKey(ProductModel, on_delete=models.CASCADE, related_name='variants', verbose_name='محصول') product = models.ForeignKey(ProductModel, on_delete=models.CASCADE, related_name='variants', verbose_name='محصول')
product_attributes = models.ManyToManyField(AttributeValue, verbose_name='ویژگی‌ها', related_name='variant') product_attributes = models.ManyToManyField(AttributeValue, verbose_name='ویژگی‌ها', related_name='variant')
in_stock = models.PositiveIntegerField(default=0, verbose_name='تعداد موجود') 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='این قیمت برای کف قیمتی محصول در نظر گرفته میشود') min_price = models.PositiveIntegerField(verbose_name='قیمت کف', help_text='این قیمت برای کف قیمتی محصول در نظر گرفته میشود')
max_price = models.PositiveIntegerField(verbose_name='قیمت سقف', help_text='این قیمت برای سقف قیمتی محصول در نظر گرفته میشود')
currency_type = ( currency_type = (
('dollor', 'دلار'), ('dollor', 'دلار'),
('toman', 'تومان'), ('toman', 'تومان'),
@@ -232,33 +231,56 @@ class ProductVariant(models.Model):
sell = models.IntegerField(default=0, verbose_name='فروش') sell = models.IntegerField(default=0, verbose_name='فروش')
currency = models.CharField(verbose_name='نوع ارز', max_length=20, choices=currency_type) currency = models.CharField(verbose_name='نوع ارز', max_length=20, choices=currency_type)
discount = models.SmallIntegerField(default=0, verbose_name='تخفیف') 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='عکس ها') images = models.ManyToManyField(ProductImageModel, verbose_name='عکس ها')
video = models.FileField(upload_to='product_videos/', blank=True, null=True, 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: class Meta:
verbose_name = 'تنوع محصول' verbose_name = 'تنوع محصول'
verbose_name_plural = 'تنوع‌های محصول' verbose_name_plural = 'تنوع‌های محصول'
def __str__(self): def __str__(self):
return f"{self.product.name} - {', '.join(str(attr) for attr in self.product_attributes.all())}" 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: if not dollor_price:
dollor_object, _ = DollorModel.objects.get_or_create(unique_filed='unique') dollor_object, _ = DollorModel.objects.get_or_create(unique_filed='unique')
dollor_price = dollor_object.price dollor_price = dollor_object.price
dollar_to_dirham = 0.27
if dollor_price is None: if dollor_price is None:
raise ValidationError({"dollor_price": "The 'dollor_price' must be provided in the context for dollar pricing."}) 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': if self.currency == 'toman':
toman_price = self.price toman_price = self.input_price
elif self.currency == 'dollor': elif self.currency == 'dollor':
toman_price = self.price * dollor_price toman_price = self.input_price * dollor_price
elif self.currency == 'derham': elif self.currency == 'derham':
toman_price = self.price * dollor_price * dollar_to_dirham toman_price = self.input_price * dollor_price * dollar_to_dirham
toman_price = toman_price if toman_price > self.min_price else self.min_price else:
return toman_price 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): 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()
+1 -6
View File
@@ -46,18 +46,13 @@ class ProductImageSerailizer(serializers.ModelSerializer):
class ProductVariantSerialzier(serializers.ModelSerializer): class ProductVariantSerialzier(serializers.ModelSerializer):
product_attributes = AttributeValueSerialzier(many=True) product_attributes = AttributeValueSerialzier(many=True)
price = serializers.SerializerMethodField()
in_pack_items = InPackItemsSerialzier(many=True) in_pack_items = InPackItemsSerialzier(many=True)
images = ProductImageSerailizer(many=True) images = ProductImageSerailizer(many=True)
details = ProductDetailSerializer(many=True, read_only=True) details = ProductDetailSerializer(many=True, read_only=True)
class Meta: class Meta:
model = ProductVariant 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): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
+1
View File
@@ -25,6 +25,7 @@ django-cleanup==8.1.0
django-colorfield==0.11.0 django-colorfield==0.11.0
django-cors-headers==4.4.0 django-cors-headers==4.4.0
django-cron==0.6.0 django-cron==0.6.0
django-crontab==0.7.1
django-dbbackup==4.2.1 django-dbbackup==4.2.1
django-dirtyfields==1.9.3 django-dirtyfields==1.9.3
django-filter==24.3 django-filter==24.3
+1 -1
View File
@@ -24,7 +24,7 @@ services:
[ [
"sh", "sh",
"-c", "-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: networks:
- default - default