mirror of
https://github.com/bringout/oca-ocb-sale.git
synced 2026-04-27 12:52:07 +02:00
19.0 vanilla
This commit is contained in:
parent
79f83631d5
commit
73afc09215
6267 changed files with 1534193 additions and 1130106 deletions
|
|
@ -1,9 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import loyalty_card
|
||||
from . import loyalty_history
|
||||
from . import loyalty_program
|
||||
from . import loyalty_reward
|
||||
from . import sale_order
|
||||
from . import sale_order_coupon_points
|
||||
from . import sale_order_line
|
||||
from . import sale_order
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
|
@ -7,8 +6,15 @@ from odoo import fields, models
|
|||
class LoyaltyCard(models.Model):
|
||||
_inherit = 'loyalty.card'
|
||||
|
||||
order_id = fields.Many2one('sale.order', 'Order Reference', readonly=True,
|
||||
help="The sales order from which coupon is generated")
|
||||
order_id = fields.Many2one(
|
||||
string="Order Reference",
|
||||
help="The sales order from which coupon is generated",
|
||||
comodel_name='sale.order',
|
||||
readonly=True,
|
||||
)
|
||||
order_id_partner_id = fields.Many2one(
|
||||
string="Sale Order Customer", comodel_name='res.partner', related='order_id.partner_id'
|
||||
)
|
||||
|
||||
def _get_default_template(self):
|
||||
default_template = super()._get_default_template()
|
||||
|
|
@ -16,11 +22,11 @@ class LoyaltyCard(models.Model):
|
|||
default_template = self.env.ref('loyalty.mail_template_loyalty_card', raise_if_not_found=False)
|
||||
return default_template
|
||||
|
||||
def _get_mail_partner(self):
|
||||
return super()._get_mail_partner() or self.order_id.partner_id
|
||||
def _mail_get_partner_fields(self, introspect_fields=False):
|
||||
return super()._mail_get_partner_fields(introspect_fields=introspect_fields) + ['order_id_partner_id']
|
||||
|
||||
def _get_mail_author(self):
|
||||
"""Default author is the order's salesperson if available, else the order's company."""
|
||||
# Default author is the order's salesperson if available, else the order's company.
|
||||
if not self.order_id or self.order_id.sudo().company_id not in self.env.companies:
|
||||
return super()._get_mail_author()
|
||||
self.ensure_one()
|
||||
|
|
@ -32,10 +38,17 @@ class LoyaltyCard(models.Model):
|
|||
def _compute_use_count(self):
|
||||
super()._compute_use_count()
|
||||
read_group_res = self.env['sale.order.line']._read_group(
|
||||
[('coupon_id', 'in', self.ids)], ['id'], ['coupon_id'])
|
||||
count_per_coupon = {r['coupon_id'][0]: r['coupon_id_count'] for r in read_group_res}
|
||||
[('coupon_id', 'in', self.ids)], ['coupon_id'], ['__count'])
|
||||
count_per_coupon = {coupon.id: count for coupon, count in read_group_res}
|
||||
for card in self:
|
||||
card.use_count += count_per_coupon.get(card.id, 0)
|
||||
|
||||
def _has_source_order(self):
|
||||
return super()._has_source_order() or bool(self.order_id)
|
||||
|
||||
def action_archive(self):
|
||||
self.env['sale.order.coupon.points'].search([
|
||||
('coupon_id', 'in', self.ids),
|
||||
('order_id.state', '=', 'draft'),
|
||||
]).unlink()
|
||||
return super().action_archive()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class LoyaltyHistory(models.Model):
|
||||
_inherit = 'loyalty.history'
|
||||
|
||||
def _get_order_portal_url(self):
|
||||
if self.order_id and self.order_model == 'sale.order':
|
||||
return self.env['sale.order'].browse(self.order_id).get_portal_url()
|
||||
return super()._get_order_portal_url()
|
||||
|
|
@ -1,21 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class LoyaltyProgram(models.Model):
|
||||
_inherit = 'loyalty.program'
|
||||
|
||||
order_count = fields.Integer(compute='_compute_order_count')
|
||||
sale_ok = fields.Boolean("Sales", default=True)
|
||||
sale_ok = fields.Boolean(string="Sales", default=True)
|
||||
|
||||
def _compute_order_count(self):
|
||||
# An order should count only once PER program but may appear in multiple programs
|
||||
read_group_res = self.env['sale.order.line'].sudo()._read_group(
|
||||
[('reward_id', 'in', self.reward_ids.ids)], ['reward_id:array_agg'], ['order_id'])
|
||||
read_group_res = self.env['sale.order.line']._read_group(
|
||||
[('reward_id', 'in', self.reward_ids.ids)], ['order_id'], ['reward_id:array_agg'])
|
||||
for program in self:
|
||||
program_reward_ids = program.reward_ids.ids
|
||||
program.order_count = sum(1 if any(id in group['reward_id'] for id in program_reward_ids) else 0 for group in read_group_res)
|
||||
program.order_count = sum(
|
||||
any(id_ in reward_ids for id_ in program_reward_ids)
|
||||
for __, reward_ids in read_group_res
|
||||
)
|
||||
|
||||
def _compute_total_order_count(self):
|
||||
super()._compute_total_order_count()
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class LoyaltyReward(models.Model):
|
||||
_inherit = 'loyalty.reward'
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,17 +1,17 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class SaleOrderCouponPoints(models.Model):
|
||||
_name = 'sale.order.coupon.points'
|
||||
_description = 'Sale Order Coupon Points - Keeps track of how a sale order impacts a coupon'
|
||||
_description = "Sale Order Coupon Points - Keeps track of how a sale order impacts a coupon"
|
||||
|
||||
order_id = fields.Many2one('sale.order', required=True, ondelete='cascade')
|
||||
coupon_id = fields.Many2one('loyalty.card', required=True, ondelete='cascade')
|
||||
order_id = fields.Many2one(comodel_name='sale.order', ondelete='cascade', required=True, index=True)
|
||||
coupon_id = fields.Many2one(comodel_name='loyalty.card', ondelete='cascade', required=True)
|
||||
points = fields.Float(required=True)
|
||||
|
||||
_sql_constraints = [
|
||||
('order_coupon_unique', 'UNIQUE (order_id, coupon_id)',
|
||||
'The coupon points entry already exists.')
|
||||
]
|
||||
_order_coupon_unique = models.Constraint(
|
||||
'UNIQUE (order_id, coupon_id)',
|
||||
"The coupon points entry already exists.",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,32 +1,40 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
class SaleOrderLine(models.Model):
|
||||
_inherit = "sale.order.line"
|
||||
|
||||
is_reward_line = fields.Boolean('Is a program reward line', compute='_compute_is_reward_line')
|
||||
reward_id = fields.Many2one('loyalty.reward', ondelete='restrict', readonly=True)
|
||||
coupon_id = fields.Many2one('loyalty.card', ondelete='restrict', readonly=True)
|
||||
reward_identifier_code = fields.Char(help="""
|
||||
Technical field used to link multiple reward lines from the same reward together.
|
||||
""")
|
||||
points_cost = fields.Float(help='How much point this reward cost on the loyalty card.')
|
||||
class SaleOrderLine(models.Model):
|
||||
_inherit = 'sale.order.line'
|
||||
|
||||
is_reward_line = fields.Boolean(
|
||||
string="Is a program reward line", compute='_compute_is_reward_line'
|
||||
)
|
||||
reward_id = fields.Many2one(
|
||||
comodel_name='loyalty.reward', ondelete='restrict', readonly=True
|
||||
)
|
||||
coupon_id = fields.Many2one(comodel_name='loyalty.card', ondelete='restrict', readonly=True)
|
||||
reward_identifier_code = fields.Char(
|
||||
help="Technical field used to link multiple reward lines from the same reward together."
|
||||
)
|
||||
points_cost = fields.Float(help="How much point this reward costs on the loyalty card.")
|
||||
|
||||
def _compute_name(self):
|
||||
# Avoid computing the name for reward lines
|
||||
reward = self.filtered('reward_id')
|
||||
super(SaleOrderLine, self - reward)._compute_name()
|
||||
|
||||
def _compute_discount(self):
|
||||
rewards = self.filtered('reward_id')
|
||||
return super(SaleOrderLine, self - rewards)._compute_discount()
|
||||
|
||||
@api.depends('reward_id')
|
||||
def _compute_is_reward_line(self):
|
||||
for line in self:
|
||||
line.is_reward_line = bool(line.reward_id)
|
||||
|
||||
def _compute_tax_id(self):
|
||||
def _compute_tax_ids(self):
|
||||
reward_lines = self.filtered('is_reward_line')
|
||||
super(SaleOrderLine, self - reward_lines)._compute_tax_id()
|
||||
super(SaleOrderLine, self - reward_lines)._compute_tax_ids()
|
||||
# Discount reward line is split per tax, the discount is set on the line but not on the product
|
||||
# as the product is the generic discount line.
|
||||
# In case of a free product, retrieving the tax on the line instead of the product won't affect the behavior.
|
||||
|
|
@ -34,8 +42,8 @@ class SaleOrderLine(models.Model):
|
|||
line = line.with_company(line.company_id)
|
||||
fpos = line.order_id.fiscal_position_id or line.order_id.fiscal_position_id._get_fiscal_position(line.order_partner_id)
|
||||
# If company_id is set, always filter taxes by the company
|
||||
taxes = line.tax_id.filtered(lambda r: not line.company_id or r.company_id == line.company_id)
|
||||
line.tax_id = fpos.map_tax(taxes)
|
||||
taxes = line.tax_ids.filtered(lambda r: not line.company_id or r.company_id == line.company_id)
|
||||
line.tax_ids = fpos.map_tax(taxes)
|
||||
|
||||
def _get_display_price(self):
|
||||
# A product created from a promotion does not have a list_price.
|
||||
|
|
@ -44,8 +52,11 @@ class SaleOrderLine(models.Model):
|
|||
return self.price_unit
|
||||
return super()._get_display_price()
|
||||
|
||||
def _is_not_sellable_line(self):
|
||||
return self.is_reward_line or super()._is_not_sellable_line()
|
||||
def _can_be_invoiced_alone(self):
|
||||
return super()._can_be_invoiced_alone() and not self.is_reward_line
|
||||
|
||||
def _is_discount_line(self):
|
||||
return super()._is_discount_line() or self.reward_id.reward_type == 'discount'
|
||||
|
||||
def _reset_loyalty(self, complete=False):
|
||||
"""
|
||||
|
|
@ -58,6 +69,7 @@ class SaleOrderLine(models.Model):
|
|||
vals = {
|
||||
'points_cost': 0,
|
||||
'price_unit': 0,
|
||||
'technical_price_unit': 0,
|
||||
}
|
||||
if complete:
|
||||
vals.update({
|
||||
|
|
@ -72,20 +84,24 @@ class SaleOrderLine(models.Model):
|
|||
res = super().create(vals_list)
|
||||
# Update our coupon points if the order is in a confirmed state
|
||||
for line in res:
|
||||
if line.coupon_id and line.points_cost and line.order_id.state in ('sale', 'done'):
|
||||
if line.coupon_id and line.points_cost and line.state == 'sale':
|
||||
line.coupon_id.points -= line.points_cost
|
||||
line.order_id._update_loyalty_history(line.coupon_id, line.points_cost)
|
||||
return res
|
||||
|
||||
def write(self, vals):
|
||||
cost_in_vals = 'points_cost' in vals
|
||||
if cost_in_vals:
|
||||
previous_cost = {l: l.points_cost for l in self}
|
||||
previous_vals = {line: (line.points_cost, line.coupon_id) for line in self}
|
||||
res = super().write(vals)
|
||||
if cost_in_vals:
|
||||
# Update our coupon points if the order is in a confirmed state
|
||||
for line in self:
|
||||
if previous_cost[line] != line.points_cost and line.order_id.state in ('sale', 'done'):
|
||||
line.coupon_id.points += (previous_cost[line] - line.points_cost)
|
||||
for line, (previous_cost, previous_coupon) in previous_vals.items():
|
||||
if line.state != 'sale':
|
||||
continue
|
||||
if line.points_cost != previous_cost or line.coupon_id != previous_coupon:
|
||||
previous_coupon.points += previous_cost
|
||||
line.coupon_id.points -= line.points_cost
|
||||
return res
|
||||
|
||||
def unlink(self):
|
||||
|
|
@ -109,7 +125,7 @@ class SaleOrderLine(models.Model):
|
|||
line.order_id.code_enabled_rule_ids = line.order_id.code_enabled_rule_ids.filtered(lambda r: r.program_id != line.coupon_id.program_id)
|
||||
# Give back the points if the order is confirmed, points are given back if the order is cancelled but in this case we need to do it directly
|
||||
for line in related_lines:
|
||||
if line.order_id.state in ('sale', 'done'):
|
||||
if line.state == 'sale':
|
||||
line.coupon_id.points += line.points_cost
|
||||
res = super(SaleOrderLine, self | related_lines).unlink()
|
||||
coupons_to_unlink.sudo().unlink()
|
||||
|
|
@ -117,3 +133,8 @@ class SaleOrderLine(models.Model):
|
|||
|
||||
def _sellable_lines_domain(self):
|
||||
return super()._sellable_lines_domain() + [('reward_id', '=', False)]
|
||||
|
||||
# === TOOLING ===#
|
||||
|
||||
def _can_be_edited_on_portal(self):
|
||||
return super()._can_be_edited_on_portal() and not self.is_reward_line
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue