mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-24 06:12:03 +02:00
19.0 vanilla
This commit is contained in:
parent
d1963a3c3a
commit
2d3ee4855a
7430 changed files with 2687981 additions and 2965473 deletions
|
|
@ -1,23 +1,23 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import ast
|
||||
import json
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.osv import expression
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.fields import Domain
|
||||
|
||||
|
||||
class LoyaltyReward(models.Model):
|
||||
_name = 'loyalty.reward'
|
||||
_description = 'Loyalty Reward'
|
||||
_description = "Loyalty Reward"
|
||||
_rec_name = 'description'
|
||||
_order = 'required_points asc'
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
def default_get(self, fields):
|
||||
# Try to copy the values of the program types default's
|
||||
result = super().default_get(fields_list)
|
||||
result = super().default_get(fields)
|
||||
if 'program_type' in self.env.context:
|
||||
program_type = self.env.context['program_type']
|
||||
program_default_values = self.env['loyalty.program']._program_type_default_values()
|
||||
|
|
@ -25,7 +25,7 @@ class LoyaltyReward(models.Model):
|
|||
len(program_default_values[program_type]['reward_ids']) == 2 and\
|
||||
isinstance(program_default_values[program_type]['reward_ids'][1][2], dict):
|
||||
result.update({
|
||||
k: v for k, v in program_default_values[program_type]['reward_ids'][1][2].items() if k in fields_list
|
||||
k: v for k, v in program_default_values[program_type]['reward_ids'][1][2].items() if k in fields
|
||||
})
|
||||
return result
|
||||
|
||||
|
|
@ -34,73 +34,116 @@ class LoyaltyReward(models.Model):
|
|||
# and makes sure to display the currency related to the program instead of the company's.
|
||||
symbol = self.env.context.get('currency_symbol', self.env.company.currency_id.symbol)
|
||||
return [
|
||||
('percent', '%'),
|
||||
('per_point', _('%s per point', symbol)),
|
||||
('per_order', _('%s per order', symbol))
|
||||
('percent', "%"),
|
||||
('per_order', symbol),
|
||||
('per_point', _("%s per point", symbol)),
|
||||
]
|
||||
|
||||
def name_get(self):
|
||||
return [(reward.id, '%s - %s' % (reward.program_id.name, reward.description)) for reward in self]
|
||||
@api.depends('program_id', 'description')
|
||||
def _compute_display_name(self):
|
||||
for reward in self:
|
||||
reward.display_name = f"{reward.program_id.name} - {reward.description}"
|
||||
|
||||
active = fields.Boolean(default=True)
|
||||
program_id = fields.Many2one('loyalty.program', required=True, ondelete='cascade')
|
||||
program_type = fields.Selection(related="program_id.program_type")
|
||||
program_id = fields.Many2one(comodel_name='loyalty.program', ondelete='cascade', required=True, index=True)
|
||||
program_type = fields.Selection(related='program_id.program_type')
|
||||
# Stored for security rules
|
||||
company_id = fields.Many2one(related='program_id.company_id', store=True)
|
||||
currency_id = fields.Many2one(related='program_id.currency_id')
|
||||
|
||||
description = fields.Char(compute='_compute_description', readonly=False, store=True, translate=True)
|
||||
description = fields.Char(
|
||||
translate=True,
|
||||
compute='_compute_description',
|
||||
precompute=True,
|
||||
store=True,
|
||||
readonly=False,
|
||||
required=True,
|
||||
)
|
||||
|
||||
reward_type = fields.Selection([
|
||||
('product', 'Free Product'),
|
||||
('discount', 'Discount')],
|
||||
default='discount', required=True,
|
||||
reward_type = fields.Selection(
|
||||
selection=[
|
||||
('product', "Free Product"),
|
||||
('discount', "Discount"),
|
||||
],
|
||||
required=True,
|
||||
default='discount',
|
||||
)
|
||||
user_has_debug = fields.Boolean(compute='_compute_user_has_debug')
|
||||
|
||||
# Discount rewards
|
||||
discount = fields.Float('Discount', default=10)
|
||||
discount_mode = fields.Selection(selection=_get_discount_mode_select, required=True, default='percent')
|
||||
discount_applicability = fields.Selection([
|
||||
('order', 'Order'),
|
||||
('cheapest', 'Cheapest Product'),
|
||||
('specific', 'Specific Products')], default='order',
|
||||
discount = fields.Float(string="Discount", default=10)
|
||||
discount_mode = fields.Selection(
|
||||
selection=_get_discount_mode_select, required=True, default='percent'
|
||||
)
|
||||
discount_applicability = fields.Selection(
|
||||
selection=[
|
||||
('order', "Order"),
|
||||
('cheapest', "Cheapest Product"),
|
||||
('specific', "Specific Products"),
|
||||
],
|
||||
default='order',
|
||||
)
|
||||
discount_product_domain = fields.Char(default="[]")
|
||||
discount_product_ids = fields.Many2many('product.product', string="Discounted Products")
|
||||
discount_product_category_id = fields.Many2one('product.category', string="Discounted Prod. Categories")
|
||||
discount_product_tag_id = fields.Many2one('product.tag', string="Discounted Prod. Tag")
|
||||
all_discount_product_ids = fields.Many2many('product.product', compute='_compute_all_discount_product_ids')
|
||||
discount_product_ids = fields.Many2many(
|
||||
string="Discounted Products", comodel_name='product.product'
|
||||
)
|
||||
discount_product_category_id = fields.Many2one(
|
||||
string="Discounted Prod. Categories", comodel_name='product.category'
|
||||
)
|
||||
discount_product_tag_id = fields.Many2one(
|
||||
string="Discounted Prod. Tag", comodel_name='product.tag'
|
||||
)
|
||||
all_discount_product_ids = fields.Many2many(
|
||||
comodel_name='product.product', compute='_compute_all_discount_product_ids'
|
||||
)
|
||||
reward_product_domain = fields.Char(compute='_compute_reward_product_domain', store=False)
|
||||
discount_max_amount = fields.Monetary('Max Discount', 'currency_id',
|
||||
help="This is the max amount this reward may discount, leave to 0 for no limit.")
|
||||
discount_line_product_id = fields.Many2one('product.product', copy=False, ondelete='restrict',
|
||||
help="Product used in the sales order to apply the discount. Each reward has its own product for reporting purpose")
|
||||
discount_max_amount = fields.Monetary(
|
||||
string="Max Discount",
|
||||
help="This is the max amount this reward may discount, leave to 0 for no limit.",
|
||||
)
|
||||
discount_line_product_id = fields.Many2one(
|
||||
help="Product used in the sales order to apply the discount. Each reward has its own"
|
||||
" product for reporting purpose",
|
||||
comodel_name='product.product',
|
||||
ondelete='restrict',
|
||||
copy=False,
|
||||
)
|
||||
is_global_discount = fields.Boolean(compute='_compute_is_global_discount')
|
||||
|
||||
# Product rewards
|
||||
reward_product_id = fields.Many2one('product.product', string='Product')
|
||||
reward_product_tag_id = fields.Many2one('product.tag', string='Product Tag')
|
||||
reward_product_id = fields.Many2one(
|
||||
string="Product", comodel_name='product.product', domain=[('type', '!=', 'combo')]
|
||||
)
|
||||
reward_product_tag_id = fields.Many2one(string="Product Tag", comodel_name='product.tag')
|
||||
multi_product = fields.Boolean(compute='_compute_multi_product')
|
||||
reward_product_ids = fields.Many2many(
|
||||
'product.product', string="Reward Products", compute='_compute_multi_product',
|
||||
string="Reward Products",
|
||||
help="These are the products that can be claimed with this rule.",
|
||||
comodel_name='product.product',
|
||||
compute='_compute_multi_product',
|
||||
search='_search_reward_product_ids',
|
||||
help="These are the products that can be claimed with this rule.")
|
||||
)
|
||||
reward_product_qty = fields.Integer(default=1)
|
||||
reward_product_uom_id = fields.Many2one('uom.uom', compute='_compute_reward_product_uom_id')
|
||||
reward_product_uom_id = fields.Many2one(
|
||||
comodel_name='uom.uom', compute='_compute_reward_product_uom_id'
|
||||
)
|
||||
|
||||
required_points = fields.Float('Points needed', default=1)
|
||||
required_points = fields.Float(string="Points needed", default=1)
|
||||
point_name = fields.Char(related='program_id.portal_point_name', readonly=True)
|
||||
clear_wallet = fields.Boolean(default=False)
|
||||
|
||||
_sql_constraints = [
|
||||
('required_points_positive', 'CHECK (required_points > 0)',
|
||||
'The required points for a reward must be strictly positive.'),
|
||||
('product_qty_positive', "CHECK (reward_type != 'product' OR reward_product_qty > 0)",
|
||||
'The reward product quantity must be strictly positive.'),
|
||||
('discount_positive', "CHECK (reward_type != 'discount' OR discount > 0)",
|
||||
'The discount must be strictly positive.'),
|
||||
]
|
||||
_required_points_positive = models.Constraint(
|
||||
'CHECK (required_points > 0)',
|
||||
"The required points for a reward must be strictly positive.",
|
||||
)
|
||||
_product_qty_positive = models.Constraint(
|
||||
"CHECK (reward_type != 'product' OR reward_product_qty > 0)",
|
||||
"The reward product quantity must be strictly positive.",
|
||||
)
|
||||
_discount_positive = models.Constraint(
|
||||
"CHECK (reward_type != 'discount' OR discount > 0)",
|
||||
"The discount must be strictly positive.",
|
||||
)
|
||||
|
||||
@api.depends('reward_product_id.product_tmpl_id.uom_id', 'reward_product_tag_id')
|
||||
def _compute_reward_product_uom_id(self):
|
||||
|
|
@ -116,17 +159,18 @@ class LoyaltyReward(models.Model):
|
|||
|
||||
def _get_discount_product_domain(self):
|
||||
self.ensure_one()
|
||||
domain = []
|
||||
constrains = []
|
||||
if self.discount_product_ids:
|
||||
domain = [('id', 'in', self.discount_product_ids.ids)]
|
||||
constrains.append([('id', 'in', self.discount_product_ids.ids)])
|
||||
if self.discount_product_category_id:
|
||||
product_category_ids = self._find_all_category_children(self.discount_product_category_id, [])
|
||||
product_category_ids.append(self.discount_product_category_id.id)
|
||||
domain = expression.OR([domain, [('categ_id', 'in', product_category_ids)]])
|
||||
constrains.append([('categ_id', 'in', product_category_ids)])
|
||||
if self.discount_product_tag_id:
|
||||
domain = expression.OR([domain, [('all_product_tag_ids', 'in', self.discount_product_tag_id.id)]])
|
||||
constrains.append([('all_product_tag_ids', 'in', self.discount_product_tag_id.id)])
|
||||
domain = Domain.OR(constrains) if constrains else Domain.TRUE
|
||||
if self.discount_product_domain and self.discount_product_domain != '[]':
|
||||
domain = expression.AND([domain, ast.literal_eval(self.discount_product_domain)])
|
||||
domain &= Domain(ast.literal_eval(self.discount_product_domain))
|
||||
return domain
|
||||
|
||||
@api.model
|
||||
|
|
@ -152,7 +196,7 @@ class LoyaltyReward(models.Model):
|
|||
if compute_all_discount_product == 'enabled':
|
||||
reward.reward_product_domain = "null"
|
||||
else:
|
||||
reward.reward_product_domain = json.dumps(reward._get_discount_product_domain())
|
||||
reward.reward_product_domain = json.dumps(list(reward._get_discount_product_domain()))
|
||||
|
||||
@api.depends('discount_product_ids', 'discount_product_category_id', 'discount_product_tag_id', 'discount_product_domain')
|
||||
def _compute_all_discount_product_ids(self):
|
||||
|
|
@ -166,13 +210,15 @@ class LoyaltyReward(models.Model):
|
|||
@api.depends('reward_product_id', 'reward_product_tag_id', 'reward_type')
|
||||
def _compute_multi_product(self):
|
||||
for reward in self:
|
||||
products = reward.reward_product_id + reward.reward_product_tag_id.product_ids
|
||||
products = reward.reward_product_id + reward.reward_product_tag_id.product_ids.filtered(
|
||||
lambda product: product.type != 'combo'
|
||||
)
|
||||
reward.multi_product = reward.reward_type == 'product' and len(products) > 1
|
||||
reward.reward_product_ids = reward.reward_type == 'product' and products or self.env['product.product']
|
||||
|
||||
def _search_reward_product_ids(self, operator, value):
|
||||
if operator not in ('=', '!=', 'in'):
|
||||
raise NotImplementedError(_("Unsupported search operator"))
|
||||
if operator != 'in':
|
||||
return NotImplemented
|
||||
return [
|
||||
'&', ('reward_type', '=', 'product'),
|
||||
'|', ('reward_product_id', operator, value),
|
||||
|
|
@ -191,57 +237,58 @@ class LoyaltyReward(models.Model):
|
|||
elif reward.reward_type == 'product':
|
||||
products = reward.reward_product_ids
|
||||
if len(products) == 0:
|
||||
reward_string = _('Free Product')
|
||||
reward_string = _("Free Product")
|
||||
elif len(products) == 1:
|
||||
reward_string = _('Free Product - %s', reward.reward_product_id.with_context(display_default_code=False).display_name)
|
||||
reward_string = _("Free Product - %s", reward.reward_product_id.with_context(display_default_code=False).display_name)
|
||||
else:
|
||||
reward_string = _('Free Product - [%s]', ', '.join(products._origin.with_context(display_default_code=False).mapped('display_name')))
|
||||
reward_string = _("Free Product - [%s]", ', '.join(products.with_context(display_default_code=False).mapped('display_name')))
|
||||
elif reward.reward_type == 'discount':
|
||||
format_string = '%(amount)g %(symbol)s'
|
||||
format_string = "%(amount)g %(symbol)s"
|
||||
if reward.currency_id.position == 'before':
|
||||
format_string = '%(symbol)s %(amount)g'
|
||||
format_string = "%(symbol)s %(amount)g"
|
||||
formatted_amount = format_string % {'amount': reward.discount, 'symbol': reward.currency_id.symbol}
|
||||
if reward.discount_mode == 'percent':
|
||||
reward_string = _('%g%% on ', reward.discount)
|
||||
reward_string = _("%g%% on ", reward.discount)
|
||||
elif reward.discount_mode == 'per_point':
|
||||
reward_string = _('%s per point on ', formatted_amount)
|
||||
reward_string = _("%s per point on ", formatted_amount)
|
||||
elif reward.discount_mode == 'per_order':
|
||||
reward_string = _('%s per order on ', formatted_amount)
|
||||
reward_string = _("%s on ", formatted_amount)
|
||||
if reward.discount_applicability == 'order':
|
||||
reward_string += _('your order')
|
||||
reward_string += _("your order")
|
||||
elif reward.discount_applicability == 'cheapest':
|
||||
reward_string += _('the cheapest product')
|
||||
reward_string += _("the cheapest product")
|
||||
elif reward.discount_applicability == 'specific':
|
||||
product_available = self.env['product.product'].search(reward._get_discount_product_domain(), limit=2)
|
||||
if len(product_available) == 1:
|
||||
reward_string += product_available.with_context(display_default_code=False).display_name
|
||||
else:
|
||||
reward_string += _('specific products')
|
||||
reward_string += _("specific products")
|
||||
if reward.discount_max_amount:
|
||||
format_string = '%(amount)g %(symbol)s'
|
||||
format_string = "%(amount)g %(symbol)s"
|
||||
if reward.currency_id.position == 'before':
|
||||
format_string = '%(symbol)s %(amount)g'
|
||||
format_string = "%(symbol)s %(amount)g"
|
||||
formatted_amount = format_string % {'amount': reward.discount_max_amount, 'symbol': reward.currency_id.symbol}
|
||||
reward_string += _(' (Max %s)', formatted_amount)
|
||||
reward_string += _(" (Max %s)", formatted_amount)
|
||||
reward.description = reward_string
|
||||
|
||||
@api.depends('reward_type', 'discount_applicability', 'discount_mode')
|
||||
def _compute_is_global_discount(self):
|
||||
for reward in self:
|
||||
reward.is_global_discount = reward.reward_type == 'discount' and\
|
||||
reward.discount_applicability == 'order' and\
|
||||
reward.discount_mode == 'percent'
|
||||
reward.is_global_discount = (
|
||||
reward.reward_type == 'discount'
|
||||
and reward.discount_applicability == 'order'
|
||||
and reward.discount_mode in ['per_order', 'percent']
|
||||
)
|
||||
|
||||
@api.depends_context('uid')
|
||||
@api.depends("reward_type")
|
||||
@api.depends('reward_type')
|
||||
def _compute_user_has_debug(self):
|
||||
self.user_has_debug = self.user_has_groups('base.group_no_one')
|
||||
self.user_has_debug = self.env.user.has_group('base.group_no_one')
|
||||
|
||||
@api.onchange('description')
|
||||
def _ensure_reward_has_description(self):
|
||||
for reward in self:
|
||||
if not reward.description:
|
||||
raise UserError(_("The reward description field cannot be empty."))
|
||||
@api.constrains('reward_product_id')
|
||||
def _check_reward_product_id_no_combo(self):
|
||||
if any(reward.reward_product_id.type == 'combo' for reward in self):
|
||||
raise ValidationError(_("A reward product can't be of type \"combo\"."))
|
||||
|
||||
def _create_missing_discount_line_products(self):
|
||||
# Make sure we create the product that will be used for our discounts
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue