from .models import ShopOrderModel, ShopOrderItem from django.db import transaction from django.db.models.signals import post_save from django.db.models.signals import pre_save from django.dispatch import receiver from .models import OrderModel from account.models import PushSubscription, UserAddressModel import ghasedak_sms from .tasks import send_change_status_notif, send_change_status_sms @receiver(pre_save, sender=OrderModel) def order_status_changed(sender, instance, **kwargs): if instance.pk: previous = OrderModel.objects.get(pk=instance.pk) if previous.status != instance.status: new_status = instance.get_status_display() # send_change_status_notif.delay(instance.pk, new_status) # send_change_status_sms.delay(instance.pk, new_status) if previous.status == 'CART' and instance.status == 'ADMIN_PENDING': # update_cart_price_fields() # update_sell_data() # update_quantity() pass @receiver(pre_save, sender=OrderModel) def set_default_address(sender, instance, **kwargs): if instance.address is None and instance.user: default_address = UserAddressModel.objects.filter( user=instance.user, is_main=True).first() if default_address: instance.address = default_address # def update_cart_price_fields(order): # pass def update_sell_data(order): pass def update_quantity(order): pass @receiver(post_save, sender=OrderModel) def create_shop_orders_on_payment(sender, instance: OrderModel, created, **kwargs): """When an order becomes paid, split it into per-shop ShopOrderModel records. This handler is safe to run multiple times (it checks if shop_orders exist). It triggers only when `is_paid` is True. """ # Only generate when order is paid and we don't already have shop orders if not instance.is_paid: return if instance.shop_orders.exists(): return # Collect order items grouped by shop items = instance.items.select_related('product__product__shop') shop_groups = {} for item in items: # product is ProductVariant -> product.product is ProductModel -> shop on ProductModel shop = None try: shop = item.product.product.shop except Exception: shop = None if not shop: # If product has no shop, skip (or you might want a default platform shop) continue shop_groups.setdefault(shop, []).append(item) if not shop_groups: return # Totals for proportional distribution shop_subtotals = {} for shop, items_list in shop_groups.items(): subtotal = 0 for it in items_list: subtotal += int(it.price) * int(it.quantity) shop_subtotals[shop] = subtotal total_subtotal = sum(shop_subtotals.values()) or 1 order_discount = int(instance.discount_amount or 0) order_special_discount = int(instance.special_discount_total or 0) order_tax = int(instance.tax or 0) with transaction.atomic(): for shop, items_list in shop_groups.items(): shop_subtotal = shop_subtotals.get(shop, 0) # proportionally allocate cart-level discount, special discount and tax allocated_discount = int( order_discount * shop_subtotal / total_subtotal) if order_discount else 0 allocated_special_discount = int( order_special_discount * shop_subtotal / total_subtotal) if order_special_discount else 0 allocated_tax = int(order_tax * shop_subtotal / total_subtotal) if order_tax else 0 commission_percent = getattr(shop, 'commission_percent', 0) or 0 try: commission_percent_value = float(commission_percent) except Exception: commission_percent_value = 0.0 # commission is calculated on the shop subtotal after discounts base_for_commission = max( 0, shop_subtotal - allocated_discount - allocated_special_discount) commission_amount = int( base_for_commission * (commission_percent_value / 100.0)) payable = shop_subtotal - allocated_discount - \ allocated_special_discount - commission_amount + allocated_tax # Prepare customer information customer_phone = instance.user.phone if instance.user else '' customer_name = instance.user.full_name if instance.user else '' # Prepare address information (with text backups in case address is deleted) address_text = '' address_postal_code = '' address_phone = '' address_city = '' address_province = '' address_recipient_name = '' if instance.address: address_text = instance.address.address address_postal_code = instance.address.postal_code address_phone = instance.address.phone address_city = instance.address.city address_province = instance.address.province address_recipient_name = instance.address.name shop_order = ShopOrderModel.objects.create( order=instance, shop=shop, customer=instance.user, customer_phone=customer_phone, customer_name=customer_name, address=instance.address, address_text=address_text, address_postal_code=address_postal_code, address_phone=address_phone, address_city=address_city, address_province=address_province, address_recipient_name=address_recipient_name, status=instance.status, is_paid=instance.is_paid, subtotal=shop_subtotal, items_count=sum(int(it.quantity) for it in items_list), discount_amount=allocated_discount, special_discount_amount=allocated_special_discount, commission_percent=commission_percent_value, commission_amount=commission_amount, tax_amount=allocated_tax, payable_amount=payable, order_created_at=instance.created_at, ) # Create ShopOrderItem rows linking to original OrderItemModel for it in items_list: ShopOrderItem.objects.create( shop_order=shop_order, order_item=it, quantity=int(it.quantity), unit_price=int(it.price), total_price=int(it.price) * int(it.quantity), discount_amount=int( it.price) * int(it.quantity) * (int(it.discount_percent or 0) / 100.0), special_discount_amount=int( it.special_discount_amount or 0), )