pos_loyalty_extend/models/sale_order.py
2026-05-28 14:50:21 +07:00

66 lines
2.9 KiB
Python

# -*- coding: utf-8 -*-
from odoo import models, api, _
from odoo.exceptions import UserError
from odoo.tools.float_utils import float_round
from odoo.fields import Command
import random
def _generate_random_reward_code():
return f"REWARD-{random.randint(100000, 999999)}"
class SaleOrder(models.Model):
_inherit = 'sale.order'
def _get_cheapest_matching_product(self, reward):
self.ensure_one()
candidate_lines = self.order_line.filtered(
lambda l: not l.is_reward_line and l.product_uom_qty > 0 and l.price_unit > 0
)
if reward.reward_product_ids:
candidate_lines = candidate_lines.filtered(
lambda l: l.product_id in reward.reward_product_ids
)
if not candidate_lines:
return None
cheapest_line = min(candidate_lines, key=lambda l: l.price_unit)
return cheapest_line.product_id
def _get_reward_values_product(self, reward, coupon, product=None, **kwargs):
self.ensure_one()
if reward.reward_product_applicability != 'cheapest':
return super()._get_reward_values_product(reward, coupon, product=product, **kwargs)
# Cheapest product applicability logic
if not product:
product = self._get_cheapest_matching_product(reward)
if not product:
# Fallback if no eligible product in cart
if reward.reward_product_ids:
product = reward.reward_product_ids[:1]
else:
first_line = self.order_line.filtered(lambda l: not l.is_reward_line and l.product_uom_qty > 0)[:1]
product = first_line.product_id
if not product:
raise UserError(_("No eligible product in the cart to apply the cheapest product reward."))
# Compute reward values directly to support global cheapest reward
taxes = self.fiscal_position_id.map_tax(product.taxes_id._filter_taxes_by_company(self.company_id))
points = self._get_real_points_for_coupon(coupon)
claimable_count = float_round(points / reward.required_points, precision_rounding=1, rounding_method='DOWN') if not reward.clear_wallet else 1
cost = points if reward.clear_wallet else claimable_count * reward.required_points
return [{
'name': reward.description or _("Free Product (Cheapest)"),
'product_id': product.id,
'discount': 100,
'product_uom_qty': reward.reward_product_qty * claimable_count,
'reward_id': reward.id,
'coupon_id': coupon.id,
'points_cost': cost,
'reward_identifier_code': _generate_random_reward_code(),
'sequence': max(self.order_line.filtered(lambda x: not x.is_reward_line).mapped('sequence'), default=10) + 1,
'tax_ids': [Command.clear()] + [Command.link(tax.id) for tax in taxes],
}]