pos_order_line_discount/models/pos_order.py
2025-09-02 15:25:52 +07:00

152 lines
6.4 KiB
Python

# -*- coding: utf-8 -*-
from odoo import models, api, fields
from odoo.tools import float_compare, float_round
import logging
_logger = logging.getLogger(__name__)
class PosOrder(models.Model):
_inherit = 'pos.order'
def _distribute_discount_to_lines(self, order, discount_amount, is_percentage=False):
"""
Distribute an order-level discount amount to individual order lines
:param order: pos.order record
:param discount_amount: The discount amount to distribute
:param is_percentage: If True, discount_amount is a percentage; otherwise it's a fixed amount
:return: Dictionary with line_id as key and discount value as value
"""
if not order.lines:
return {}
# Filter out reward lines and lines with zero price
regular_lines = order.lines.filtered(lambda l: not l.is_reward_line and l.price_subtotal > 0)
if not regular_lines:
return {}
# Calculate total tax-exclusive amount for proportional distribution
total_amount = sum(line.price_subtotal for line in regular_lines)
if total_amount <= 0:
return {}
line_discounts = {}
if is_percentage:
# Apply the same percentage discount to all regular lines
for line in regular_lines:
# For percentage discounts, we simply add the percentages (simplified approach)
# In a real scenario, you might want to compound them properly
new_discount = min(100, line.discount + discount_amount)
line_discounts[line.id] = new_discount
else:
# Distribute fixed amount proportionally based on line tax-exclusive subtotal
remaining_discount = discount_amount
lines_count = len(regular_lines)
for i, line in enumerate(regular_lines):
if i == lines_count - 1:
# Last line gets the remaining discount to avoid rounding issues
# Calculate the additional discount percentage for this line based on tax-exclusive price
if line.price_subtotal > 0:
additional_discount_percentage = (remaining_discount / line.price_subtotal) * 100
new_discount = min(100, line.discount + additional_discount_percentage)
else:
new_discount = line.discount
line_discounts[line.id] = new_discount
else:
# Calculate proportional discount for this line based on tax-exclusive price
line_ratio = line.price_subtotal / total_amount if total_amount > 0 else 0
line_discount_amount = discount_amount * line_ratio
# Calculate the additional discount percentage for this line based on tax-exclusive price
if line.price_subtotal > 0:
additional_discount_percentage = (line_discount_amount / line.price_subtotal) * 100
new_discount = min(100, line.discount + additional_discount_percentage)
else:
new_discount = line.discount
line_discounts[line.id] = new_discount
remaining_discount -= line_discount_amount
return line_discounts
def _apply_line_discounts(self, order, line_discounts):
"""
Apply calculated discounts to order lines
:param order: pos.order record
:param line_discounts: Dictionary with line_id as key and discount value as value
"""
for line in order.lines:
if line.id in line_discounts:
# Apply the calculated discount
line.discount = line_discounts[line.id]
# Trigger the onchange to recalculate the line amounts
line._onchange_amount_line_all()
@api.model
def _process_order_rewards_as_line_discounts(self, order):
"""
Process loyalty rewards as line discounts instead of order-level discounts
:param order: pos.order record
"""
if not order.config_id.apply_line_discount_on_rewards:
return
# Find reward lines
reward_lines = order.lines.filtered(lambda l: l.is_reward_line)
for reward_line in reward_lines:
reward = reward_line.reward_id
if reward and reward.reward_type == 'discount':
# Calculate the discount amount that should be applied to regular lines
reward_amount = abs(reward_line.price_subtotal)
# Remove the reward line
reward_line.unlink()
# Distribute the discount to regular lines
is_percentage = (reward.discount_mode == 'percent')
discount_value = reward_amount if not is_percentage else reward.discount
line_discounts = self._distribute_discount_to_lines(
order,
discount_value,
is_percentage=is_percentage
)
# Apply the discounts to lines
self._apply_line_discounts(order, line_discounts)
@api.model
def _process_order(self, order, draft, existing_order):
"""
Override to process rewards as line discounts
"""
pos_order_id = super(PosOrder, self)._process_order(order, draft, existing_order)
# Convert order-level rewards to line discounts
if isinstance(pos_order_id, int):
pos_order = self.browse(pos_order_id)
self._process_order_rewards_as_line_discounts(pos_order)
return pos_order_id
class PosOrderLine(models.Model):
_inherit = 'pos.order.line'
def _compute_amount_line_all(self):
"""
Override to handle line-level discounts properly
"""
res = super(PosOrderLine, self)._compute_amount_line_all()
# Additional logic for line discounts can be added here if needed
return res
@api.onchange('discount')
def _onchange_discount(self):
"""
Override to handle line discount changes
"""
super(PosOrderLine, self)._onchange_discount()
# Additional logic for handling line discount changes can be added here