summaryrefslogtreecommitdiff
path: root/addons/website_sale_coupon/models
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/website_sale_coupon/models
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/website_sale_coupon/models')
-rw-r--r--addons/website_sale_coupon/models/__init__.py6
-rw-r--r--addons/website_sale_coupon/models/sale_coupon.py13
-rw-r--r--addons/website_sale_coupon/models/sale_coupon_program.py36
-rw-r--r--addons/website_sale_coupon/models/sale_order.py95
4 files changed, 150 insertions, 0 deletions
diff --git a/addons/website_sale_coupon/models/__init__.py b/addons/website_sale_coupon/models/__init__.py
new file mode 100644
index 00000000..3ac5d489
--- /dev/null
+++ b/addons/website_sale_coupon/models/__init__.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import sale_coupon
+from . import sale_coupon_program
+from . import sale_order
diff --git a/addons/website_sale_coupon/models/sale_coupon.py b/addons/website_sale_coupon/models/sale_coupon.py
new file mode 100644
index 00000000..0b8029ef
--- /dev/null
+++ b/addons/website_sale_coupon/models/sale_coupon.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import models
+
+
+class SaleCoupon(models.Model):
+ _inherit = 'coupon.coupon'
+
+ def _check_coupon_code(self, order):
+ if self.program_id.website_id and self.program_id.website_id != order.website_id:
+ return {'error': 'This coupon is not valid on this website.'}
+ return super()._check_coupon_code(order)
diff --git a/addons/website_sale_coupon/models/sale_coupon_program.py b/addons/website_sale_coupon/models/sale_coupon_program.py
new file mode 100644
index 00000000..b55d2a0e
--- /dev/null
+++ b/addons/website_sale_coupon/models/sale_coupon_program.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, models, _
+from odoo.exceptions import ValidationError
+
+
+class CouponProgram(models.Model):
+ _name = 'coupon.program'
+ _inherit = ['coupon.program', 'website.multi.mixin']
+
+ @api.constrains('promo_code', 'website_id')
+ def _check_promo_code_constraint(self):
+ """ Only case where multiple same code could coexists is if they all belong to their own website.
+ If the program is website generic, we should ensure there is no generic and no specific (even for other website) already
+ If the program is website specific, we should ensure there is no existing code for this website or False
+ """
+ for program in self.filtered(lambda p: p.promo_code):
+ domain = [('id', '!=', program.id), ('promo_code', '=', program.promo_code)]
+ if program.website_id:
+ domain += program.website_id.website_domain()
+ if self.search(domain):
+ raise ValidationError(_('The program code must be unique by website!'))
+
+ def _filter_programs_on_website(self, order):
+ return self.filtered(lambda program: not program.website_id or program.website_id.id == order.website_id.id)
+
+ @api.model
+ def _filter_programs_from_common_rules(self, order, next_order=False):
+ programs = self._filter_programs_on_website(order)
+ return super(CouponProgram, programs)._filter_programs_from_common_rules(order, next_order)
+
+ def _check_promo_code(self, order, coupon_code):
+ if self.website_id and self.website_id != order.website_id:
+ return {'error': 'This promo code is not valid on this website.'}
+ return super()._check_promo_code(order, coupon_code)
diff --git a/addons/website_sale_coupon/models/sale_order.py b/addons/website_sale_coupon/models/sale_order.py
new file mode 100644
index 00000000..08ff0123
--- /dev/null
+++ b/addons/website_sale_coupon/models/sale_order.py
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+from datetime import timedelta
+
+from odoo import api, fields, models
+from odoo.http import request
+
+
+class SaleOrder(models.Model):
+ _inherit = "sale.order"
+
+ def _compute_website_order_line(self):
+ """ This method will merge multiple discount lines generated by a same program
+ into a single one (temporary line with `new()`).
+ This case will only occur when the program is a discount applied on multiple
+ products with different taxes.
+ In this case, each taxes will have their own discount line. This is required
+ to have correct amount of taxes according to the discount.
+ But we wan't these lines to be `visually` merged into a single one in the
+ e-commerce since the end user should only see one discount line.
+ This is only possible since we don't show taxes in cart.
+ eg:
+ line 1: 10% discount on product with tax `A` - $15
+ line 2: 10% discount on product with tax `B` - $11.5
+ line 3: 10% discount on product with tax `C` - $10
+ would be `hidden` and `replaced` by
+ line 1: 10% discount - $36.5
+
+ Note: The line will be created without tax(es) and the amount will be computed
+ depending if B2B or B2C is enabled.
+ """
+ super()._compute_website_order_line()
+ for order in self:
+ # TODO: potential performance bottleneck downstream
+ programs = order._get_applied_programs_with_rewards_on_current_order()
+ for program in programs:
+ program_lines = order.order_line.filtered(lambda line:
+ line.product_id == program.discount_line_product_id)
+ if len(program_lines) > 1:
+ if self.env.user.has_group('sale.group_show_price_subtotal'):
+ price_unit = sum(program_lines.mapped('price_subtotal'))
+ else:
+ price_unit = sum(program_lines.mapped('price_total'))
+ # TODO: batch then flush
+ order.website_order_line += self.env['sale.order.line'].new({
+ 'product_id': program_lines[0].product_id.id,
+ 'price_unit': price_unit,
+ 'name': program_lines[0].name,
+ 'product_uom_qty': 1,
+ 'product_uom': program_lines[0].product_uom.id,
+ 'order_id': order.id,
+ 'is_reward_line': True,
+ })
+ order.website_order_line -= program_lines
+
+ def _compute_cart_info(self):
+ super(SaleOrder, self)._compute_cart_info()
+ for order in self:
+ reward_lines = order.website_order_line.filtered(lambda line: line.is_reward_line)
+ order.cart_quantity -= int(sum(reward_lines.mapped('product_uom_qty')))
+
+ def get_promo_code_error(self, delete=True):
+ error = request.session.get('error_promo_code')
+ if error and delete:
+ request.session.pop('error_promo_code')
+ return error
+
+ def _cart_update(self, product_id=None, line_id=None, add_qty=0, set_qty=0, **kwargs):
+ res = super(SaleOrder, self)._cart_update(product_id=product_id, line_id=line_id, add_qty=add_qty, set_qty=set_qty, **kwargs)
+ self.recompute_coupon_lines()
+ return res
+
+ def _get_free_shipping_lines(self):
+ self.ensure_one()
+ free_shipping_prgs_ids = self._get_applied_programs_with_rewards_on_current_order().filtered(lambda p: p.reward_type == 'free_shipping')
+ if not free_shipping_prgs_ids:
+ return self.env['sale.order.line']
+ free_shipping_product_ids = free_shipping_prgs_ids.mapped('discount_line_product_id')
+ return self.order_line.filtered(lambda l: l.product_id in free_shipping_product_ids)
+
+ @api.autovacuum
+ def _gc_abandoned_coupons(self, *args, **kwargs):
+ """Remove/free coupon from abandonned ecommerce order."""
+ ICP = self.env['ir.config_parameter']
+ validity = ICP.get_param('website_sale_coupon.abandonned_coupon_validity', 4)
+ validity = fields.Datetime.to_string(fields.datetime.now() - timedelta(days=int(validity)))
+ coupon_to_reset = self.env['coupon.coupon'].search([
+ ('state', '=', 'used'),
+ ('sales_order_id.state', '=', 'draft'),
+ ('sales_order_id.write_date', '<', validity),
+ ('sales_order_id.website_id', '!=', False),
+ ])
+ for coupon in coupon_to_reset:
+ coupon.sales_order_id.applied_coupon_ids -= coupon
+ coupon_to_reset.write({'state': 'new'})
+ coupon_to_reset.mapped('sales_order_id').recompute_coupon_lines()