mirror of
https://github.com/bringout/oca-ocb-sale.git
synced 2026-04-27 13:12:07 +02:00
Initial commit: Sale packages
This commit is contained in:
commit
14e3d26998
6469 changed files with 2479670 additions and 0 deletions
|
|
@ -0,0 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import loyalty_card
|
||||
from . import loyalty_program
|
||||
from . import loyalty_reward
|
||||
from . import sale_order_coupon_points
|
||||
from . import sale_order_line
|
||||
from . import sale_order
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
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")
|
||||
|
||||
def _get_default_template(self):
|
||||
default_template = super()._get_default_template()
|
||||
if not default_template:
|
||||
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 _get_mail_author(self):
|
||||
"""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()
|
||||
return (self.order_id.user_id or self.order_id.company_id).partner_id
|
||||
|
||||
def _get_signature(self):
|
||||
return self.order_id.user_id.signature or super()._get_signature()
|
||||
|
||||
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}
|
||||
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)
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# -*- 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)
|
||||
|
||||
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'])
|
||||
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)
|
||||
|
||||
def _compute_total_order_count(self):
|
||||
super()._compute_total_order_count()
|
||||
for program in self:
|
||||
program.total_order_count += program.order_count
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# -*- 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'
|
||||
|
||||
def _get_discount_product_values(self):
|
||||
res = super()._get_discount_product_values()
|
||||
for vals in res:
|
||||
vals.update({
|
||||
'taxes_id': False,
|
||||
'supplier_taxes_id': False,
|
||||
'invoice_policy': 'order',
|
||||
})
|
||||
return res
|
||||
|
||||
def unlink(self):
|
||||
if len(self) == 1 and self.env['sale.order.line'].sudo().search_count([('reward_id', 'in', self.ids)], limit=1):
|
||||
return self.action_archive()
|
||||
return super().unlink()
|
||||
1131
odoo-bringout-oca-ocb-sale_loyalty/sale_loyalty/models/sale_order.py
Normal file
1131
odoo-bringout-oca-ocb-sale_loyalty/sale_loyalty/models/sale_order.py
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +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'
|
||||
|
||||
order_id = fields.Many2one('sale.order', required=True, ondelete='cascade')
|
||||
coupon_id = fields.Many2one('loyalty.card', required=True, ondelete='cascade')
|
||||
points = fields.Float(required=True)
|
||||
|
||||
_sql_constraints = [
|
||||
('order_coupon_unique', 'UNIQUE (order_id, coupon_id)',
|
||||
'The coupon points entry already exists.')
|
||||
]
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
# -*- 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.')
|
||||
|
||||
def _compute_name(self):
|
||||
# Avoid computing the name for reward lines
|
||||
reward = self.filtered('reward_id')
|
||||
super(SaleOrderLine, self - reward)._compute_name()
|
||||
|
||||
@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):
|
||||
reward_lines = self.filtered('is_reward_line')
|
||||
super(SaleOrderLine, self - reward_lines)._compute_tax_id()
|
||||
# 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.
|
||||
for line in reward_lines:
|
||||
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)
|
||||
|
||||
def _get_display_price(self):
|
||||
# A product created from a promotion does not have a list_price.
|
||||
# The price_unit of a reward order line is computed by the promotion, so it can be used directly
|
||||
if self.is_reward_line and self.reward_id.reward_type != 'product':
|
||||
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 _reset_loyalty(self, complete=False):
|
||||
"""
|
||||
Reset the line(s) to a state which does not impact reward computation.
|
||||
If complete is set to True we also remove the coupon and reward from the line(s).
|
||||
This option should be used when the line will be unlinked.
|
||||
|
||||
Returns self
|
||||
"""
|
||||
vals = {
|
||||
'points_cost': 0,
|
||||
'price_unit': 0,
|
||||
}
|
||||
if complete:
|
||||
vals.update({
|
||||
'coupon_id': False,
|
||||
'reward_id': False,
|
||||
})
|
||||
self.write(vals)
|
||||
return self
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
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'):
|
||||
line.coupon_id.points -= 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}
|
||||
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)
|
||||
return res
|
||||
|
||||
def unlink(self):
|
||||
# Remove related reward lines
|
||||
reward_coupon_set = {(l.reward_id, l.coupon_id, l.reward_identifier_code) for l in self if l.reward_id}
|
||||
related_lines = self.env['sale.order.line']
|
||||
related_lines |= self.order_id.order_line.filtered(lambda l: (l.reward_id, l.coupon_id, l.reward_identifier_code) in reward_coupon_set)
|
||||
# Remove the line's coupon from order if it is the last line using that coupon
|
||||
coupons_to_unlink = self.env['loyalty.card']
|
||||
for line in self:
|
||||
if line.coupon_id:
|
||||
# 2 cases:
|
||||
# case 1: coupon has been applied directly
|
||||
# case 2: coupon was created from a program
|
||||
if line.coupon_id in line.order_id.applied_coupon_ids:
|
||||
line.order_id.applied_coupon_ids -= line.coupon_id
|
||||
elif line.coupon_id.order_id == line.order_id and line.coupon_id.program_id.applies_on == 'current' and\
|
||||
not any(oLine.coupon_id == line.coupon_id and oLine not in related_lines for oLine in line.order_id.order_line):
|
||||
# ondelete='restrict' would prevent deletion of the coupon unlink after unlinking lines
|
||||
coupons_to_unlink |= line.coupon_id
|
||||
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'):
|
||||
line.coupon_id.points += line.points_cost
|
||||
res = super(SaleOrderLine, self | related_lines).unlink()
|
||||
coupons_to_unlink.sudo().unlink()
|
||||
return res
|
||||
|
||||
def _sellable_lines_domain(self):
|
||||
return super()._sellable_lines_domain() + [('reward_id', '=', False)]
|
||||
Loading…
Add table
Add a link
Reference in a new issue