You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
8.6 KiB
155 lines
8.6 KiB
# -*- coding: utf-8 -*-
|
|
###################################################################################
|
|
#
|
|
# Cybrosys Technologies Pvt. Ltd.
|
|
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
|
|
# Author: Saneen K (odoo@cybrosys.com)
|
|
#
|
|
#
|
|
# This program is free software: you can modify
|
|
# it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) as
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU LESSER GENERAL PUBLIC LICENSE for more details.
|
|
#
|
|
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
#
|
|
###################################################################################
|
|
|
|
from ast import literal_eval
|
|
from odoo import models, _
|
|
from odoo.tools.float_utils import float_round
|
|
|
|
|
|
class SaleOrder(models.Model):
|
|
"""Inheriting the sale order for adding customer domain"""
|
|
_inherit = 'sale.order'
|
|
|
|
def _program_check_compute_points(self, programs):
|
|
"""
|
|
Checks the program validity from the order lines aswell as computing the number of points to add.
|
|
|
|
Returns a dict containing the error message or the points that will be given with the keys 'points'.
|
|
|
|
Checks the validity of customer domain
|
|
"""
|
|
self.ensure_one()
|
|
|
|
# Prepare quantities
|
|
order_lines = self.order_line.filtered(lambda line: line.product_id and not line.reward_id)
|
|
products = order_lines.product_id
|
|
products_qties = dict.fromkeys(products, 0)
|
|
for line in order_lines:
|
|
products_qties[line.product_id] += line.product_uom_qty
|
|
# Contains the products that can be applied per rule
|
|
products_per_rule = programs._get_valid_products(products)
|
|
|
|
# Prepare amounts
|
|
no_effect_lines = self._get_no_effect_on_threshold_lines()
|
|
base_untaxed_amount = self.amount_untaxed - sum(line.price_subtotal for line in no_effect_lines)
|
|
base_tax_amount = self.amount_tax - sum(line.price_tax for line in no_effect_lines)
|
|
amounts_per_program = {p: {'untaxed': base_untaxed_amount, 'tax': base_tax_amount} for p in programs}
|
|
# Customer set to False initially
|
|
customer = False
|
|
for line in self.order_line:
|
|
if not line.reward_id or line.reward_id.reward_type != 'discount':
|
|
continue
|
|
for program in programs:
|
|
# Do not consider the program's discount + automatic discount lines for the amount to check.
|
|
if customer and line.reward_id.program_id.trigger == 'auto' or line.reward_id.program_id == program:
|
|
amounts_per_program[program]['untaxed'] -= line.price_subtotal
|
|
amounts_per_program[program]['tax'] -= line.price_tax
|
|
|
|
result = {}
|
|
# Check if the customer domain and sale order is same and set to True
|
|
for program in programs:
|
|
for rec in program.rule_ids:
|
|
if rec.customer_domain and rec.customer_domain != '[]':
|
|
customers = self.env['res.partner'].search(literal_eval(rec.customer_domain))
|
|
if self.partner_id in customers:
|
|
customer = True
|
|
|
|
untaxed_amount = amounts_per_program[program]['untaxed']
|
|
tax_amount = amounts_per_program[program]['tax']
|
|
|
|
# Used for error messages
|
|
# By default False, but True if no rules and applies_on current -> misconfigured coupons program
|
|
code_matched = not bool(program.rule_ids) and program.applies_on == 'current' # Stays false if all
|
|
# triggers have code and none have been activated
|
|
minimum_amount_matched = code_matched
|
|
product_qty_matched = code_matched
|
|
points = 0
|
|
# Some rules may split their points per unit / money spent
|
|
# (i.e. gift cards 2x50$ must result in two 50$ codes)
|
|
rule_points = []
|
|
program_result = result.setdefault(program, dict())
|
|
for rule in program.rule_ids:
|
|
if rule.mode == 'with_code' and rule not in self.code_enabled_rule_ids and not customer:
|
|
continue
|
|
code_matched = True
|
|
|
|
rule_amount = rule._compute_amount(self.currency_id)
|
|
if rule_amount > (rule.minimum_amount_tax_mode == 'incl' and (untaxed_amount + tax_amount) or
|
|
untaxed_amount) and not customer:
|
|
continue
|
|
minimum_amount_matched = True
|
|
rule_products = products_per_rule[rule]
|
|
ordered_rule_products_qty = sum(products_qties[product] for product in rule_products)
|
|
if ordered_rule_products_qty < rule.minimum_qty or not rule_products and not customer:
|
|
continue
|
|
product_qty_matched = True
|
|
if not rule.reward_point_amount and customer:
|
|
continue
|
|
# Count all points separately if the order is for the future and the split option is enabled
|
|
if program.applies_on == 'future' and rule.reward_point_split and rule.reward_point_mode != 'order':
|
|
if rule.reward_point_mode == 'unit' and customer:
|
|
rule_points.extend(rule.reward_point_amount for _ in range(int(ordered_rule_products_qty)))
|
|
elif rule.reward_point_mode == 'money':
|
|
for line in self.order_line:
|
|
if line.is_reward_line or line.product_id not in rule_products or \
|
|
line.product_uom_qty <= 0 and not customer:
|
|
continue
|
|
points_per_unit = float_round(
|
|
(rule.reward_point_amount * line.price_total / line.product_uom_qty),
|
|
precision_digits=2, rounding_method='DOWN')
|
|
if not points_per_unit:
|
|
continue
|
|
rule_points.extend([points_per_unit] * int(line.product_uom_qty))
|
|
else:
|
|
# All checks have been passed we can now compute the points to give
|
|
if rule.reward_point_mode == 'order' and customer:
|
|
points += rule.reward_point_amount
|
|
elif rule.reward_point_mode == 'money' and customer:
|
|
# Compute amount paid for rule
|
|
# NOTE: this does not account for discounts -> 1 point per $ * (100$ - 30%)
|
|
# will result in 100 points
|
|
amount_paid = sum(max(0, line.price_total)
|
|
for line in order_lines if line.product_id in rule_products)
|
|
points += float_round(rule.reward_point_amount * amount_paid,
|
|
precision_digits=2, rounding_method='DOWN')
|
|
elif rule.reward_point_mode == 'unit' and customer:
|
|
points += rule.reward_point_amount * ordered_rule_products_qty
|
|
# NOTE: for programs that are nominative we always allow the program to be 'applied' on the order
|
|
# with 0 points so that `_get_claimable_rewards` returns the rewards associated with those programs
|
|
if not program.is_nominative:
|
|
if not code_matched:
|
|
program_result['error'] = _("This program requires a code to be applied.")
|
|
elif not minimum_amount_matched:
|
|
program_result['error'] = _(
|
|
'A minimum of %(amount)s %(currency)s should be purchased to get the reward',
|
|
amount=min(program.rule_ids.mapped('minimum_amount')),
|
|
currency=program.currency_id.name,
|
|
)
|
|
elif not product_qty_matched:
|
|
program_result['error'] = _("You don't have the required product quantities on your sales order.")
|
|
elif not self._allow_nominative_programs():
|
|
program_result['error'] = _("This program is not available for public users.")
|
|
if 'error' not in program_result:
|
|
points_result = [points] + rule_points
|
|
program_result['points'] = points_result
|
|
return result
|
|
|