diff --git a/backend/order/signals.py b/backend/order/signals.py index 2f46ac5..c157a1c 100644 --- a/backend/order/signals.py +++ b/backend/order/signals.py @@ -8,6 +8,7 @@ from account.models import PushSubscription, UserAddressModel import ghasedak_sms from .tasks import send_change_status_notif, send_change_status_sms, send_shop_order_invoice_telegram_task from django.conf import settings +from decimal import Decimal @receiver(pre_save, sender=OrderModel) @@ -82,7 +83,7 @@ def create_shop_orders_on_payment(sender, instance: OrderModel, created, **kwarg if not shop_groups: return - # Totals for proportional distribution + # Totals for proportional distribution (for cart-level discount and tax only) shop_subtotals = {} for shop, items_list in shop_groups.items(): subtotal = 0 @@ -93,43 +94,45 @@ def create_shop_orders_on_payment(sender, instance: OrderModel, created, **kwarg 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) - # Calculate total item-level discounts for this shop - item_level_discounts = 0 + # Calculate total item-level discounts for this shop (discount_percent) + item_level_discounts = Decimal('0') for it in items_list: - item_discount = int(it.price) * int(it.quantity) * (int(it.discount_percent or 0) / 100.0) - item_level_discounts += int(item_discount) + item_discount = Decimal(str(it.price)) * Decimal(str(it.quantity)) * (Decimal(str(it.discount_percent or 0)) / Decimal('100')) + item_level_discounts += item_discount - # 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 + # Calculate total item-level special discounts for this shop + # (already calculated as profit * special_discount_percent) + item_special_discounts = Decimal('0') + for it in items_list: + item_special_discounts += Decimal(str(it.special_discount_amount or 0)) + + # Proportionally allocate cart-level discount and tax + allocated_discount = (Decimal(str(order_discount)) * Decimal(str(shop_subtotal)) / Decimal(str(total_subtotal))) if order_discount else Decimal('0') + allocated_tax = (Decimal(str(order_tax)) * Decimal(str(shop_subtotal)) / Decimal(str(total_subtotal))) if order_tax else Decimal('0') commission_percent = getattr(shop, 'commission_percent', 0) or 0 try: - commission_percent_value = float(commission_percent) + commission_percent_value = Decimal(str(commission_percent)) except Exception: - commission_percent_value = 0.0 + commission_percent_value = Decimal('0') - # commission is calculated on the subtotal after ALL discounts (item-level, cart-level, and special) - # but BEFORE tax + # Commission is calculated on the subtotal after ALL discounts: + # subtotal - item_discount - item_special_discount - cart_discount base_for_commission = max( - 0, shop_subtotal - item_level_discounts - allocated_discount - allocated_special_discount) - commission_amount = int( - base_for_commission * (commission_percent_value / 100.0)) + Decimal('0'), + Decimal(str(shop_subtotal)) - item_level_discounts - item_special_discounts - allocated_discount + ) + commission_amount = base_for_commission * (commission_percent_value / Decimal('100')) # Payable to shop: subtotal minus all discounts minus commission (no tax added to payable) - payable = shop_subtotal - item_level_discounts - allocated_discount - \ - allocated_special_discount - commission_amount + payable = Decimal(str(shop_subtotal)) - item_level_discounts - item_special_discounts - \ + allocated_discount - commission_amount # Prepare customer information customer_phone = (instance.user.phone or '') if instance.user else '' @@ -182,28 +185,27 @@ def create_shop_orders_on_payment(sender, instance: OrderModel, created, **kwarg is_paid=instance.is_paid, subtotal=shop_subtotal, items_count=sum(int(it.quantity) for it in items_list), - discount_amount=allocated_discount, + discount_amount=int(allocated_discount), special_discount_code=instance.special_discount_code, - special_discount_amount=allocated_special_discount, - commission_percent=commission_percent_value, - commission_amount=commission_amount, - tax_amount=allocated_tax, - payable_amount=payable, + special_discount_amount=int(item_special_discounts), + commission_percent=float(commission_percent_value), + commission_amount=int(commission_amount), + tax_amount=int(allocated_tax), + payable_amount=int(payable), order_created_at=order_created_datetime, ) # Create ShopOrderItem rows linking to original OrderItemModel for it in items_list: + item_discount_amount = Decimal(str(it.price)) * Decimal(str(it.quantity)) * (Decimal(str(it.discount_percent or 0)) / Decimal('100')) 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), + discount_amount=int(item_discount_amount), + special_discount_amount=int(it.special_discount_amount or 0), )