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.
		
		
		
		
		
			
		
			
				
					
					
						
							174 lines
						
					
					
						
							9.2 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							174 lines
						
					
					
						
							9.2 KiB
						
					
					
				| # -*- coding: utf-8 -*- | |
| ################################################################################ | |
| # | |
| #    Cybrosys Technologies Pvt. Ltd. | |
| #    Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). | |
| #    Author: Subina (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 as well 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
 | |
| 
 |