refactor discount calculations in create_shop_orders_on_payment to use Decimal for precision
This commit is contained in:
+34
-32
@@ -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),
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user