[IMP] sale_coupon: coupon layout and usability improve
some layout fixed and confusing string of promo_applicability option is changed to 'Send a Coupon' form 'Apply On Next Order'. Task-ID: 2027296 Closes #34620
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class MailComposeMessage(models.TransientModel):
|
||||
_inherit = 'mail.compose.message'
|
||||
|
||||
def send_mail(self, **kwargs):
|
||||
for wizard in self:
|
||||
if self._context.get('mark_coupon_as_sent') and wizard.model == 'sale.coupon' and wizard.partner_ids:
|
||||
self.env[wizard.model].browse(wizard.res_id).state = 'sent'
|
||||
return super().send_mail(**kwargs)
|
||||
@@ -53,11 +53,14 @@ class SaleCoupon(models.Model):
|
||||
def _check_coupon_code(self, order):
|
||||
message = {}
|
||||
applicable_programs = order._get_applicable_programs()
|
||||
if self.state in ('used', 'expired') or \
|
||||
(self.expiration_date and self.expiration_date < order.date_order.date()):
|
||||
message = {'error': _('This coupon %s has been used or is expired.') % (self.code)}
|
||||
if self.state == 'used':
|
||||
message = {'error': _('This coupon has already been used (%s).') % (self.code)}
|
||||
elif self.state == 'reserved':
|
||||
message = {'error': _('This coupon %s exists but the origin sales order is not validated yet.') % (self.code)}
|
||||
elif self.state == 'cancel':
|
||||
message = {'error': _('This coupon has been cancelled (%s).') % (self.code)}
|
||||
elif self.state == 'expired' or (self.expiration_date and self.expiration_date < order.date_order.date()):
|
||||
message = {'error': _('This coupon is expired (%s).') % (self.code)}
|
||||
# Minimum requirement should not be checked if the coupon got generated by a promotion program (the requirement should have only be checked to generate the coupon)
|
||||
elif self.program_id.program_type == 'coupon_program' and not self.program_id._filter_on_mimimum_amount(order):
|
||||
message = {'error': _('A minimum of %s %s should be purchased to get the reward') % (self.program_id.rule_minimum_amount, self.program_id.currency_id.name)}
|
||||
|
||||
@@ -43,14 +43,14 @@ class SaleCouponProgram(models.Model):
|
||||
help="A promotion code is a code that is associated with a marketing discount. For example, a retailer might tell frequent customers to enter the promotion code 'THX001' to receive a 10%% discount on their whole order.")
|
||||
promo_applicability = fields.Selection([
|
||||
('on_current_order', 'Apply On Current Order'),
|
||||
('on_next_order', 'Apply On Next Order')],
|
||||
('on_next_order', 'Send a Coupon')],
|
||||
default='on_current_order', string="Applicability")
|
||||
coupon_ids = fields.One2many('sale.coupon', 'program_id', string="Generated Coupons", copy=False)
|
||||
coupon_count = fields.Integer(compute='_compute_coupon_count')
|
||||
order_count = fields.Integer(compute='_compute_order_count')
|
||||
company_id = fields.Many2one('res.company', string="Company", default=lambda self: self.env.company)
|
||||
currency_id = fields.Many2one(string="Currency", related='company_id.currency_id', readonly=True)
|
||||
validity_duration = fields.Integer(default=1,
|
||||
validity_duration = fields.Integer(default=30,
|
||||
help="Validity duration for a coupon after its generation")
|
||||
|
||||
@api.constrains('promo_code')
|
||||
|
||||
@@ -66,10 +66,12 @@
|
||||
<img alt="Barcode" t-att-src="'/report/barcode/Code128/%s' % o.code"/>
|
||||
<br/>
|
||||
<div class="mt64">
|
||||
<div class="col-6 text-right">
|
||||
<div class="text-center">
|
||||
<img alt="Logo" t-att-src="'/logo?company=%d' % (o.program_id.company_id)" t-att-alt="'%s' % (o.program_id.company_id.name)" style="border:0px solid transparent; height: 50; width: 200px;" height="50"/>
|
||||
</div>
|
||||
<div class="col-6 text-left">
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-center d-inline-block">
|
||||
<span t-field="o.program_id.company_id.partner_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "email"], "no_marker": True}'/>
|
||||
</div>
|
||||
|
||||
@@ -232,12 +232,6 @@
|
||||
<group>
|
||||
<field name="promo_applicability" widget="radio"/>
|
||||
</group>
|
||||
<group>
|
||||
<span class="oe_grey">
|
||||
<b>Apply on Current Order -</b> Reward will be applied on current order.<br/>
|
||||
<b>Apply on Next Order -</b> Generate a coupon for a next order.
|
||||
</span>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -50,5 +50,5 @@ class SaleCouponApplyCode(models.TransientModel):
|
||||
order.applied_coupon_ids += coupon
|
||||
coupon.write({'state': 'used'})
|
||||
else:
|
||||
error_status = {'not_found': _('The code %s is invalid') % (coupon_code)}
|
||||
error_status = {'not_found': _('This coupon is invalid (%s).') % (coupon_code)}
|
||||
return error_status
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from odoo import api, fields, models
|
||||
|
||||
import ast
|
||||
from odoo.osv import expression
|
||||
|
||||
|
||||
class SaleCouponGenerate(models.TransientModel):
|
||||
@@ -15,6 +16,7 @@ class SaleCouponGenerate(models.TransientModel):
|
||||
('nbr_customer', 'Number of Selected Customers')
|
||||
], default='nbr_coupon')
|
||||
partners_domain = fields.Char(string="Customer", default='[]')
|
||||
has_partner_email = fields.Boolean(compute='_compute_has_partner_email')
|
||||
|
||||
def generate_coupon(self):
|
||||
"""Generates the number of coupons entered in wizard field nbr_coupons
|
||||
@@ -29,9 +31,16 @@ class SaleCouponGenerate(models.TransientModel):
|
||||
|
||||
if self.generation_type == 'nbr_customer' and self.partners_domain:
|
||||
for partner in self.env['res.partner'].search(ast.literal_eval(self.partners_domain)):
|
||||
vals.update({'partner_id': partner.id})
|
||||
vals.update({'partner_id': partner.id, 'state': 'sent' if partner.email else 'new'})
|
||||
coupon = self.env['sale.coupon'].create(vals)
|
||||
subject = '%s, a coupon has been generated for you' % (partner.name)
|
||||
template = self.env.ref('sale_coupon.mail_template_sale_coupon', raise_if_not_found=False)
|
||||
if template:
|
||||
template.send_mail(coupon.id, email_values={'email_to': partner.email, 'email_from': self.env.user.email or '', 'subject': subject,})
|
||||
email_values = {'email_to': partner.email, 'email_from': self.env.user.email or '', 'subject': subject}
|
||||
template.send_mail(coupon.id, email_values=email_values, notif_layout='mail.mail_notification_light')
|
||||
|
||||
@api.depends('partners_domain')
|
||||
def _compute_has_partner_email(self):
|
||||
for record in self:
|
||||
domain = expression.AND([safe_eval(record.partners_domain), [('email', '=', False)]])
|
||||
record.has_partner_email = self.env['res.partner'].search_count(domain) == 0
|
||||
|
||||
@@ -6,10 +6,14 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Generate Coupons">
|
||||
<group>
|
||||
<field name="has_partner_email" invisible="1"/>
|
||||
<field name="generation_type" widget="radio"/>
|
||||
<field name="partners_domain" attrs="{'invisible': [('generation_type', '!=', 'nbr_customer')]}" widget="domain" options="{'model': 'res.partner'}"/>
|
||||
<field name="nbr_coupons" attrs="{'invisible': [('generation_type', '!=', 'nbr_coupon')]}"/>
|
||||
</group>
|
||||
<div role="alert" class="alert alert-warning" attrs="{'invisible': ['|', ('generation_type', '!=', 'nbr_customer'), ('has_partner_email', '=', True)]}">
|
||||
Some selected customers do not have an email address and will not receive the coupon.
|
||||
</div>
|
||||
<footer>
|
||||
<button name="generate_coupon" type="object" string="Generate" class="oe_highlight"/>
|
||||
<button special="cancel" string="Cancel"/>
|
||||
|
||||
Reference in New Issue
Block a user