feat: add daily report functionality and settlement status to shop orders
This commit is contained in:
+37
-6
@@ -1,6 +1,7 @@
|
|||||||
from django.contrib import admin, messages
|
from django.contrib import admin, messages
|
||||||
from .models import *
|
from .models import *
|
||||||
from unfold.admin import TabularInline, StackedInline
|
from unfold.admin import TabularInline, StackedInline
|
||||||
|
from unfold.contrib.inlines.admin import NonrelatedTabularInline
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from import_export.admin import ImportExportModelAdmin
|
from import_export.admin import ImportExportModelAdmin
|
||||||
from unfold.contrib.import_export.forms import ExportForm, ImportForm, SelectableFieldsExportForm
|
from unfold.contrib.import_export.forms import ExportForm, ImportForm, SelectableFieldsExportForm
|
||||||
@@ -60,19 +61,50 @@ class CartAdmin(ModelAdmin):
|
|||||||
inlines = [CartItemInline]
|
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
|
from .models import ShopDailyReport, ShopOrderModel
|
||||||
@admin.register(ShopDailyReport)
|
@admin.register(ShopDailyReport)
|
||||||
class ShopDailyReportAdmin(ModelAdmin):
|
class ShopDailyReportAdmin(ModelAdmin):
|
||||||
pass
|
list_display = ['shop', 'date', 'is_settled',]
|
||||||
|
inlines = [ShopOrderInline, ShopOrderItemInline]
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
|
|
||||||
if request.user.is_superuser:
|
if request.user.is_superuser:
|
||||||
return ShopOrderModel.objects.all()
|
return self.model.objects.all()
|
||||||
|
|
||||||
if not hasattr(request.user, 'shop'):
|
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
|
return queryset
|
||||||
|
|
||||||
def has_view_permission(self, request, obj=None):
|
def has_view_permission(self, request, obj=None):
|
||||||
@@ -95,11 +127,10 @@ class ShopOrderItemInline(StackedInline):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(ShopOrderModel)
|
@admin.register(ShopOrderModel)
|
||||||
class ShopOrderModelAdmin(ShopOrderAdminPermission, ModelAdmin):
|
class ShopOrderModelAdmin(ShopOrderAdminPermission, ModelAdmin):
|
||||||
inlines = [ShopOrderItemInline]
|
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']
|
readonly_fields = ['download_invoice_link']
|
||||||
|
|
||||||
def download_invoice_button(self, obj):
|
def download_invoice_button(self, obj):
|
||||||
|
|||||||
@@ -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='گزارش روزانه'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -279,6 +279,7 @@ class ShopOrderModel(models.Model):
|
|||||||
"""
|
"""
|
||||||
order = models.ForeignKey(OrderModel, on_delete=models.CASCADE, related_name='shop_orders')
|
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')
|
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 Information
|
||||||
customer = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, verbose_name='مشتری')
|
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_sales = models.BigIntegerField(default=0)
|
||||||
total_commission = models.BigIntegerField(default=0)
|
total_commission = models.BigIntegerField(default=0)
|
||||||
total_payable = 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)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from .models import ShopOrderModel, ShopOrderItem
|
from .models import ShopOrderModel, ShopOrderItem, ShopDailyReport
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
from django.db.models.signals import pre_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,
|
shop_order_id=instance.pk,
|
||||||
chat_id=instance.shop.telegram_chat_id,
|
chat_id=instance.shop.telegram_chat_id,
|
||||||
bot_token=bot_token
|
bot_token=bot_token
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|||||||
@@ -75,9 +75,9 @@ def generate_daily_shop_reports():
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
from .models import ShopOrderModel, ShopDailyReport
|
from .models import ShopOrderModel, ShopDailyReport
|
||||||
|
|
||||||
target_date = (timezone.now() - timedelta(days=1)).date()
|
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)
|
shop_orders = ShopOrderModel.objects.filter(created_at__date=target_date)
|
||||||
if not shop_orders.exists():
|
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:
|
if created:
|
||||||
reports_created += 1
|
reports_created += 1
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ django-iranian-cities==1.0.2
|
|||||||
django-jalali==7.3.0
|
django-jalali==7.3.0
|
||||||
django-storages==1.14.5
|
django-storages==1.14.5
|
||||||
django-timezone-field==7.1
|
django-timezone-field==7.1
|
||||||
django-unfold==0.48.0
|
django-unfold==0.78.1
|
||||||
djangorestframework==3.15.2
|
djangorestframework==3.15.2
|
||||||
djangorestframework-simplejwt==5.3.1
|
djangorestframework-simplejwt==5.3.1
|
||||||
djoser==2.3.1
|
djoser==2.3.1
|
||||||
|
|||||||
Reference in New Issue
Block a user