90 lines
4.0 KiB
Python
90 lines
4.0 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_id or reward.reward_product_tag_id or reward.reward_product_category_id:
|
|
allowed_products = reward.reward_product_id
|
|
if reward.reward_product_tag_id:
|
|
allowed_products |= reward.reward_product_tag_id.product_ids
|
|
|
|
category_ids = []
|
|
if reward.reward_product_category_id:
|
|
category_ids = self.env['product.category'].search([
|
|
('id', 'child_of', reward.reward_product_category_id.ids)
|
|
]).ids
|
|
|
|
candidate_lines = candidate_lines.filtered(
|
|
lambda l: l.product_id in allowed_products or (
|
|
category_ids and l.product_id.categ_id.id in category_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
|
|
allowed_products = reward.reward_product_id
|
|
if reward.reward_product_tag_id:
|
|
allowed_products |= reward.reward_product_tag_id.product_ids
|
|
if reward.reward_product_category_id:
|
|
category_ids = self.env['product.category'].search([
|
|
('id', 'child_of', reward.reward_product_category_id.ids)
|
|
]).ids
|
|
allowed_products |= self.env['product.product'].search([
|
|
('categ_id', 'in', category_ids),
|
|
('type', '!=', 'combo')
|
|
], limit=1)
|
|
|
|
if allowed_products:
|
|
product = allowed_products[: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],
|
|
}]
|