152 lines
6.4 KiB
Python
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
|