# -*- 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