diff --git a/backend/order/admin.py b/backend/order/admin.py index dbb0f86..44ceae5 100644 --- a/backend/order/admin.py +++ b/backend/order/admin.py @@ -1,6 +1,7 @@ from django.contrib import admin, messages from .models import * from unfold.admin import TabularInline, StackedInline +from unfold.contrib.inlines.admin import NonrelatedTabularInline from django.db.models import Q from import_export.admin import ImportExportModelAdmin from unfold.contrib.import_export.forms import ExportForm, ImportForm, SelectableFieldsExportForm @@ -60,19 +61,50 @@ class CartAdmin(ModelAdmin): inlines = [CartItemInline] +class ShopOrderInline(StackedInline): + model = ShopOrderModel + extra = 0 + max_num = 0 + tab = True + + +class ShopOrderItemInline(NonrelatedTabularInline): # NonrelatedStackedInline is available as well + model = ShopOrderItem + tab = True + extra = 0 + show_change_link = True + + def get_form_queryset(self, obj): + """ + Gets all nonrelated objects needed for inlines. Method must be implemented. + """ + shop_orders = obj.shop_orders.all() + + return ShopOrderItem.objects.filter(shop_order__in=shop_orders) + + def save_new_instance(self, parent, instance): + """ + Extra save method which can for example update inline instances based on current + main model object. Method must be implemented. + """ + pass + + + from .models import ShopDailyReport, ShopOrderModel @admin.register(ShopDailyReport) class ShopDailyReportAdmin(ModelAdmin): - pass + list_display = ['shop', 'date', 'is_settled',] + inlines = [ShopOrderInline, ShopOrderItemInline] def get_queryset(self, request): if request.user.is_superuser: - return ShopOrderModel.objects.all() + return self.model.objects.all() if not hasattr(request.user, 'shop'): - return ShopOrderModel.objects.none() + return self.model.objects.none() - queryset = ShopOrderModel.objects.filter(shop=request.user.shop) + queryset = self.model.objects.filter(shop=request.user.shop) return queryset def has_view_permission(self, request, obj=None): @@ -95,11 +127,10 @@ class ShopOrderItemInline(StackedInline): - @admin.register(ShopOrderModel) class ShopOrderModelAdmin(ShopOrderAdminPermission, ModelAdmin): inlines = [ShopOrderItemInline] - list_display = ['id', 'shop', 'order', 'customer_name', 'status', 'is_paid', 'is_settled', 'download_invoice_button'] + list_display = ['id', 'shop', 'order', 'customer_name', 'status', 'is_paid', 'is_settled', 'created_at', 'download_invoice_button'] readonly_fields = ['download_invoice_link'] def download_invoice_button(self, obj): diff --git a/backend/order/migrations/0043_shopdailyreport_is_settled_and_more.py b/backend/order/migrations/0043_shopdailyreport_is_settled_and_more.py new file mode 100644 index 0000000..e68df67 --- /dev/null +++ b/backend/order/migrations/0043_shopdailyreport_is_settled_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.2 on 2026-02-08 15:40 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('order', '0042_ordermodel_special_discount_code_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='shopdailyreport', + name='is_settled', + field=models.BooleanField(default=False, verbose_name='تسویه شده'), + ), + migrations.AddField( + model_name='shopordermodel', + name='daily_report', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='shop_orders', to='order.shopdailyreport', verbose_name='گزارش روزانه'), + ), + ] diff --git a/backend/order/models.py b/backend/order/models.py index bfc1257..eaaba4c 100644 --- a/backend/order/models.py +++ b/backend/order/models.py @@ -279,6 +279,7 @@ class ShopOrderModel(models.Model): """ order = models.ForeignKey(OrderModel, on_delete=models.CASCADE, related_name='shop_orders') shop = models.ForeignKey('account.ShopModel', on_delete=models.CASCADE, related_name='shop_orders') + daily_report = models.ForeignKey('ShopDailyReport', on_delete=models.SET_NULL, null=True, blank=True, related_name='shop_orders', verbose_name='گزارش روزانه') # Customer Information customer = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, verbose_name='مشتری') @@ -355,6 +356,7 @@ class ShopDailyReport(models.Model): total_sales = models.BigIntegerField(default=0) total_commission = models.BigIntegerField(default=0) total_payable = models.BigIntegerField(default=0) + is_settled = models.BooleanField(default=False, verbose_name='تسویه شده') created_at = models.DateTimeField(auto_now_add=True) class Meta: diff --git a/backend/order/signals.py b/backend/order/signals.py index 121ac35..acff8a3 100644 --- a/backend/order/signals.py +++ b/backend/order/signals.py @@ -1,4 +1,4 @@ -from .models import ShopOrderModel, ShopOrderItem +from .models import ShopOrderModel, ShopOrderItem, ShopDailyReport from django.db import transaction from django.db.models.signals import post_save from django.db.models.signals import pre_save @@ -248,4 +248,11 @@ def send_invoice_to_shop_telegram(sender, instance: ShopOrderModel, created, **k shop_order_id=instance.pk, chat_id=instance.shop.telegram_chat_id, bot_token=bot_token - ) \ No newline at end of file + ) + + +@receiver(post_save, sender=ShopDailyReport) +def update_shop_orders_settlement_status(sender, instance: ShopDailyReport, **kwargs): + """When a ShopDailyReport's is_settled status changes, update all related ShopOrderModel instances.""" + # Update all shop orders linked to this daily report + instance.shop_orders.update(is_settled=instance.is_settled) diff --git a/backend/order/tasks.py b/backend/order/tasks.py index 9dd8466..a19edab 100644 --- a/backend/order/tasks.py +++ b/backend/order/tasks.py @@ -75,9 +75,9 @@ def generate_daily_shop_reports(): from datetime import timedelta from django.db.models import Sum from .models import ShopOrderModel, ShopDailyReport - + target_date = (timezone.now() - timedelta(days=1)).date() - logging.info(f'Generating shop reports for {target_date}') + print(f'Generating shop reports for {target_date}') shop_orders = ShopOrderModel.objects.filter(created_at__date=target_date) if not shop_orders.exists(): @@ -110,6 +110,9 @@ def generate_daily_shop_reports(): } ) + # Link all shop orders for this shop and date to the daily report + shop_orders.filter(shop_id=shop_id).update(daily_report=report) + if created: reports_created += 1 else: diff --git a/backend/requirements.txt b/backend/requirements.txt index 1062c77..5d1c4e4 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -42,7 +42,7 @@ django-iranian-cities==1.0.2 django-jalali==7.3.0 django-storages==1.14.5 django-timezone-field==7.1 -django-unfold==0.48.0 +django-unfold==0.78.1 djangorestframework==3.15.2 djangorestframework-simplejwt==5.3.1 djoser==2.3.1