[MERGE] point_of_sale, pos_loyalty: merging the pos_loyalty module
Conflicts: addons/point_of_sale/static/src/js/widgets.js
This commit is contained in:
committed by
Frédéric van der Essen
parent
f2fdaa017c
commit
ee28165972
@@ -506,6 +506,7 @@ td {
|
||||
|
||||
.pos .control-buttons {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding: 8px 13px 0px 11px;
|
||||
}
|
||||
.pos .control-button {
|
||||
@@ -521,6 +522,7 @@ td {
|
||||
font-size: 18px;
|
||||
margin-left: 6px;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
.pos .control-button .fa{
|
||||
margin-right: 4px;
|
||||
@@ -1921,6 +1923,17 @@ td {
|
||||
box-sizing: border-box;
|
||||
width: 80%;
|
||||
}
|
||||
.pos .popup.popup-selection .selection {
|
||||
overflow-y: auto;
|
||||
max-height: 270px;
|
||||
}
|
||||
.pos .popup.popup-selection .selection .button {
|
||||
float: none;
|
||||
display: block;
|
||||
width: auto;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
/* ********* The Webkit Scrollbar ********* */
|
||||
|
||||
.pos *::-webkit-scrollbar{
|
||||
|
||||
@@ -375,6 +375,13 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
|
||||
}else{
|
||||
var model = self.models[index];
|
||||
self.pos_widget.loading_message(_t('Loading')+' '+(model.label || model.model || ''), progress);
|
||||
|
||||
var cond = typeof model.condition === 'function' ? model.condition(self,tmp) : true;
|
||||
if (!cond) {
|
||||
load_model(index+1);
|
||||
return;
|
||||
}
|
||||
|
||||
var fields = typeof model.fields === 'function' ? model.fields(self,tmp) : model.fields;
|
||||
var domain = typeof model.domain === 'function' ? model.domain(self,tmp) : model.domain;
|
||||
var context = typeof model.context === 'function' ? model.context(self,tmp) : model.context;
|
||||
|
||||
@@ -378,6 +378,53 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* A popup that allows the user to select one item from a list.
|
||||
*
|
||||
* show_popup('selection',{
|
||||
* message: 'Pick an Option',
|
||||
* message: "Popup Title",
|
||||
* list: [
|
||||
* { label: 'foobar', item: 45 },
|
||||
* { label: 'bar foo', item: 'stuff' },
|
||||
* ],
|
||||
* confirm: function(item) {
|
||||
* // get the item selected by the user.
|
||||
* },
|
||||
* cancel: function(){
|
||||
* // user chose nothing
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
|
||||
module.SelectionPopupWidget = module.PopUpWidget.extend({
|
||||
template: 'SelectionPopupWidget',
|
||||
show: function(options){
|
||||
var self = this;
|
||||
this._super();
|
||||
|
||||
this.message = options.message || '';
|
||||
this.list = options.list || [];
|
||||
this.renderElement();
|
||||
|
||||
this.$('.button.cancel').click(function(){
|
||||
self.pos_widget.screen_selector.close_popup();
|
||||
if (options.cancel){
|
||||
options.cancel.call(self);
|
||||
}
|
||||
});
|
||||
|
||||
this.$('.button.item').click(function(){
|
||||
self.pos_widget.screen_selector.close_popup();
|
||||
if (options.confirm) {
|
||||
var item = self.list[parseInt($(this).data('item-index'))];
|
||||
item = item ? item.item : item;
|
||||
options.confirm.call(self,item);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
module.TextInputPopupWidget = module.PopUpWidget.extend({
|
||||
template: 'TextInputPopupWidget',
|
||||
show: function(options){
|
||||
|
||||
@@ -1006,6 +1006,9 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
|
||||
this.fullscreen_popup = new module.FullscreenPopup(this,{});
|
||||
this.fullscreen_popup.appendTo(this.$el);
|
||||
|
||||
this.selection_popup = new module.SelectionPopupWidget(this,{});
|
||||
this.selection_popup.appendTo(this.$el);
|
||||
|
||||
this.textinput_popup = new module.TextInputPopupWidget(this,{});
|
||||
this.textinput_popup.appendTo(this.$el);
|
||||
|
||||
@@ -1081,6 +1084,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
|
||||
'textinput': this.textinput_popup,
|
||||
'confirm': this.confirm_popup,
|
||||
'fullscreen': this.fullscreen_popup,
|
||||
'selection': this.selection_popup,
|
||||
'unsent-orders': this.unsent_orders_popup,
|
||||
},
|
||||
default_screen: 'products',
|
||||
|
||||
@@ -958,6 +958,26 @@
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="SelectionPopupWidget">
|
||||
<div class="modal-dialog">
|
||||
<div class="popup popup-selection">
|
||||
<p class="message"><t t-esc=" widget.message || 'Confirm ?' " /></p>
|
||||
<div class='selection'>
|
||||
<t t-foreach="widget.list || []" t-as="item">
|
||||
<div class='button item' t-att-data-item-index='item_index'>
|
||||
<t t-esc='item.label'/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="button cancel">
|
||||
Cancel
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="Product">
|
||||
<span class='product' t-att-data-product-id="product.id">
|
||||
<div class="product-img">
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import loyalty
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
|
||||
{
|
||||
'name': 'Loyalty Program',
|
||||
'version': '1.0',
|
||||
'category': 'Point of Sale',
|
||||
'sequence': 6,
|
||||
'summary': 'Loyalty Program for the Point of Sale ',
|
||||
'description': """
|
||||
|
||||
=======================
|
||||
|
||||
This module allows you to define a loyalty program in
|
||||
the point of sale, where the customers earn loyalty points
|
||||
and get rewards.
|
||||
|
||||
""",
|
||||
'author': 'OpenERP SA',
|
||||
'depends': ['point_of_sale'],
|
||||
'data': [
|
||||
'views/views.xml',
|
||||
'views/templates.xml'
|
||||
],
|
||||
'qweb': ['static/src/xml/loyalty.xml'],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
}
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -0,0 +1,132 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import logging
|
||||
|
||||
import openerp
|
||||
|
||||
from openerp import tools
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class loyalty_program(osv.osv):
|
||||
_name = 'loyalty.program'
|
||||
_columns = {
|
||||
'name' : fields.char('Loyalty Program Name', size=32, select=1,
|
||||
required=True, help="An internal identification for the loyalty program configuration"),
|
||||
'pp_currency': fields.float('Points per currency',help="How many loyalty points are given to the customer by sold currency"),
|
||||
'pp_product': fields.float('Points per product',help="How many loyalty points are given to the customer by product sold"),
|
||||
'pp_order': fields.float('Points per order',help="How many loyalty points are given to the customer for each sale or order"),
|
||||
'rounding': fields.float('Points Rounding', help="The loyalty point amounts are rounded to multiples of this value."),
|
||||
'rule_ids': fields.one2many('loyalty.rule','loyalty_program_id','Rules'),
|
||||
'reward_ids': fields.one2many('loyalty.reward','loyalty_program_id','Rewards'),
|
||||
|
||||
}
|
||||
_defaults = {
|
||||
'rounding': 1,
|
||||
}
|
||||
|
||||
class loyalty_rule(osv.osv):
|
||||
_name = 'loyalty.rule'
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=32, select=1, required=True, help="An internal identification for this loyalty program rule"),
|
||||
'loyalty_program_id': fields.many2one('loyalty.program', 'Loyalty Program', help='The Loyalty Program this exception belongs to'),
|
||||
'product_id': fields.many2one('product.product','Target Product', help='The product affected by the rule'),
|
||||
'override': fields.boolean('Override Base Rules', help='A product targetted by this rule will be ignored by other loyalty rules, including the default one'),
|
||||
'pp_product': fields.float('Points per product', help='How many points the product will earn per product ordered'),
|
||||
'pp_currency': fields.float('Points per currency', help='How many points the product will earn per value sold'),
|
||||
}
|
||||
|
||||
class loyalty_reward(osv.osv):
|
||||
_name = 'loyalty.reward'
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=32, select=1, required=True, help='An internal identification for this loyalty reward'),
|
||||
'loyalty_program_id': fields.many2one('loyalty.program', 'Loyalty Program', help='The Loyalty Program this reward belongs to'),
|
||||
'minimum_points': fields.float('Minimum Points', help='The minimum amount of points the customer must have to be eligible for this reward'),
|
||||
'type': fields.selection((('gift','Gift'),('discount','Discount')), 'Type', required=True, help='The type of the reward'),
|
||||
'gift_product_id': fields.many2one('product.product','Gift Product', help='The product given as a reward'),
|
||||
'point_cost': fields.float('Point Cost', help='The cost of the reward'),
|
||||
'discount_product_id': fields.many2one('product.product','Discount Product', help='The product used to apply discounts'),
|
||||
'discount': fields.float('Discount',help='The discount percentage'),
|
||||
}
|
||||
|
||||
def _check_gift_product(self, cr, uid, ids, context=None):
|
||||
for reward in self.browse(cr, uid, ids, context=context):
|
||||
print reward.type
|
||||
if reward.type == 'gift':
|
||||
return bool(reward.gift_product_id)
|
||||
else:
|
||||
return True
|
||||
|
||||
def _check_discount_product(self, cr, uid, ids, context=None):
|
||||
for reward in self.browse(cr, uid, ids, context=context):
|
||||
print reward.type
|
||||
if reward.type == 'discount':
|
||||
return bool(reward.discount_product_id)
|
||||
else:
|
||||
return True
|
||||
|
||||
_constraints = [
|
||||
(_check_gift_product, "The gift product field is mandatory for gift rewards", ["type","gift_product_id"]),
|
||||
(_check_discount_product, "The discount product field is mandatory for discount rewards", ["type","discount_product_id"]),
|
||||
]
|
||||
|
||||
class pos_config(osv.osv):
|
||||
_inherit = 'pos.config'
|
||||
_columns = {
|
||||
'loyalty_id': fields.many2one('loyalty.program','Loyalty Program', help='The loyalty program used by this point_of_sale'),
|
||||
|
||||
}
|
||||
|
||||
class res_partner(osv.osv):
|
||||
_inherit = 'res.partner'
|
||||
_columns = {
|
||||
'loyalty_points': fields.float('Loyalty Points', help='The loyalty points the user won as part of a Loyalty Program')
|
||||
}
|
||||
|
||||
class pos_order(osv.osv):
|
||||
_inherit = 'pos.order'
|
||||
|
||||
_columns = {
|
||||
'loyalty_points': fields.float('Loyalty Points', help='The amount of Loyalty points the customer won or lost with this order'),
|
||||
}
|
||||
|
||||
def _order_fields(self, cr, uid, ui_order, context=None):
|
||||
fields = super(pos_order,self)._order_fields(cr,uid,ui_order,context)
|
||||
fields['loyalty_points'] = ui_order.get('loyalty_points',0)
|
||||
return fields
|
||||
|
||||
def create_from_ui(self, cr, uid, orders, context=None):
|
||||
ids = super(pos_order,self).create_from_ui(cr,uid,orders,context=context)
|
||||
for order in orders:
|
||||
if order['data']['loyalty_points'] != 0 and order['data']['partner_id']:
|
||||
partner = self.pool.get('res.partner').browse(cr,uid,order['data']['partner_id'], context=context)
|
||||
partner.write({'loyalty_points': partner['loyalty_points'] + order['data']['loyalty_points']})
|
||||
|
||||
return ids
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
.pos .order .summary .loyalty-points{
|
||||
margin-left: 20px;
|
||||
float: left;
|
||||
padding: 10px;
|
||||
max-width: 216px;
|
||||
text-align: left;
|
||||
color: #6EC89B;
|
||||
background: rgba(110, 200, 155, 0.17);
|
||||
border-radius: 3px;
|
||||
}
|
||||
.pos .order .summary .loyalty-points.negative{
|
||||
color: #C86E6E;
|
||||
background: rgba(200, 110, 110, 0.17);
|
||||
}
|
||||
.pos .order .summary .loyalty-points-total {
|
||||
border-top: solid 2px;
|
||||
text-align: center;
|
||||
padding-top: 4px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
openerp.pos_loyalty = function(instance){
|
||||
|
||||
var module = instance.point_of_sale;
|
||||
var round_pr = instance.web.round_precision
|
||||
var QWeb = instance.web.qweb;
|
||||
|
||||
var models = module.PosModel.prototype.models;
|
||||
for (var i = 0; i < models.length; i++) {
|
||||
var model = models[i];
|
||||
if (model.model === 'product.product') {
|
||||
model.fields.push('loyalty_points');
|
||||
model.fields.push('loyalty_override');
|
||||
} else if (model.model === 'res.partner') {
|
||||
model.fields.push('loyalty_points');
|
||||
} else if (model.model === 'pos.config') {
|
||||
// load loyalty after pos.config
|
||||
models.splice(i+1,0,{
|
||||
model: 'loyalty.program',
|
||||
condition: function(self){ return !!self.config.loyalty_id[0]; },
|
||||
fields: ['name','pp_currency','pp_product','pp_order','rounding'],
|
||||
domain: function(self){ return [['id','=',self.config.loyalty_id[0]]]; },
|
||||
loaded: function(self,loyalties){ self.loyalty = loyalties[0]; },
|
||||
},{
|
||||
model: 'loyalty.rule',
|
||||
condition: function(self){ return !!self.loyalty; },
|
||||
fields: ['name','type','product_id','category_id','override','pp_product','pp_currency'],
|
||||
domain: function(self){ return [['loyalty_program_id','=',self.loyalty.id]]; },
|
||||
loaded: function(self,rules){
|
||||
|
||||
self.loyalty.rules = rules;
|
||||
self.loyalty.rules_by_product_id = {};
|
||||
|
||||
for (var i = 0; i < rules.length; i++){
|
||||
var rule = rules[i];
|
||||
if (!self.loyalty.rules_by_product_id[rule.product_id[0]]) {
|
||||
self.loyalty.rules_by_product_id[rule.product_id[0]] = [rule];
|
||||
} else if (rule.override) {
|
||||
self.loyalty.rules_by_product_id[rule.product_id[0]].unshift(rule);
|
||||
} else {
|
||||
self.loyalty.rules_by_product_id[rule.product_id[0]].push(rule);
|
||||
}
|
||||
}
|
||||
},
|
||||
},{
|
||||
model: 'loyalty.reward',
|
||||
condition: function(self){ return !!self.loyalty; },
|
||||
fields: ['name','type','minimum_points','gift_product_id','point_cost','discount_product_id','discount'],
|
||||
domain: function(self){ return [['loyalty_program_id','=',self.loyalty.id]]; },
|
||||
loaded: function(self,rewards){
|
||||
self.loyalty.rewards = rewards;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var _super = module.Order;
|
||||
module.Order = module.Order.extend({
|
||||
|
||||
/* The total of points won, excluding the points spent on rewards */
|
||||
get_won_points: function(){
|
||||
if (!this.pos.loyalty || !this.get('client')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var orderLines = this.get('orderLines').models;
|
||||
var rounding = this.pos.loyalty.rounding;
|
||||
|
||||
var product_sold = 0;
|
||||
var total_sold = 0;
|
||||
var total_points = 0;
|
||||
|
||||
for (var i = 0; i < orderLines.length; i++) {
|
||||
var line = orderLines[i];
|
||||
var product = line.get_product();
|
||||
var rules = this.pos.loyalty.rules_by_product_id[product.id] || [];
|
||||
var overriden = false;
|
||||
|
||||
if (line.reward) { // Reward products are ignored
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var j = 0; j < rules.length; j++) {
|
||||
var rule = rules[j];
|
||||
total_points += round_pr(line.get_quantity() * rule.pp_product, rounding);
|
||||
total_points += round_pr(line.get_price_with_tax() * rule.pp_currency, rounding);
|
||||
if (rule.override) {
|
||||
overriden = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!overriden) {
|
||||
product_sold += line.get_quantity();
|
||||
total_sold += line.get_price_with_tax();
|
||||
}
|
||||
}
|
||||
|
||||
total_points += round_pr( total_sold * this.pos.loyalty.pp_currency, rounding );
|
||||
total_points += round_pr( product_sold * this.pos.loyalty.pp_product, rounding );
|
||||
total_points += round_pr( this.pos.loyalty.pp_order, rounding );
|
||||
|
||||
return total_points;
|
||||
},
|
||||
|
||||
/* The total number of points spent on rewards */
|
||||
get_spent_points: function() {
|
||||
if (!this.pos.loyalty || !this.get('client')) {
|
||||
return 0;
|
||||
} else {
|
||||
var lines = this.get('orderLines').models;
|
||||
var rounding = this.pos.loyalty.rounding;
|
||||
var points = 0;
|
||||
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var line = lines[i];
|
||||
if (line.reward) {
|
||||
if (line.reward.type === 'gift') {
|
||||
points += round_pr(line.get_quantity() * line.reward.point_cost, rounding);
|
||||
} else if (line.reward.type === 'discount') {
|
||||
points += round_pr(-line.get_price_with_tax() * line.reward.point_cost, rounding);
|
||||
}
|
||||
}
|
||||
}
|
||||
return points;
|
||||
}
|
||||
},
|
||||
|
||||
/* The total number of points lost or won after the order is validated */
|
||||
get_new_points: function() {
|
||||
if (!this.pos.loyalty || !this.get('client')) {
|
||||
return 0;
|
||||
} else {
|
||||
return round_pr(this.get_won_points() - this.get_spent_points(), this.pos.loyalty.rounding);
|
||||
}
|
||||
},
|
||||
|
||||
/* The total number of points that the customer will have after this order is validated */
|
||||
get_new_total_points: function() {
|
||||
if (!this.pos.loyalty || !this.get('client')) {
|
||||
return 0;
|
||||
} else {
|
||||
return round_pr(this.get('client').loyalty_points + this.get_new_points(), this.pos.loyalty.rounding);
|
||||
}
|
||||
},
|
||||
|
||||
/* The number of loyalty points currently owned by the customer */
|
||||
get_current_points: function(){
|
||||
return this.get('client') ? this.get('client').loyalty_points : 0;
|
||||
},
|
||||
|
||||
/* The total number of points spendable on rewards */
|
||||
get_spendable_points: function(){
|
||||
if (!this.pos.loyalty || !this.get('client')) {
|
||||
return 0;
|
||||
} else {
|
||||
return round_pr(this.get('client').loyalty_points - this.get_spent_points(), this.pos.loyalty.rounding);
|
||||
}
|
||||
},
|
||||
|
||||
/* The list of rewards that the current customer can get */
|
||||
get_available_rewards: function(){
|
||||
var client = this.get('client');
|
||||
if (!client) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var rewards = [];
|
||||
for (var i = 0; i < this.pos.loyalty.rewards.length; i++) {
|
||||
var reward = this.pos.loyalty.rewards[i];
|
||||
if (reward.minimum_points > this.get_spendable_points()) {
|
||||
continue;
|
||||
} else if(reward.type === 'gift' && reward.point_cost > this.get_spendable_points()) {
|
||||
continue;
|
||||
}
|
||||
rewards.push(reward);
|
||||
}
|
||||
return rewards;
|
||||
},
|
||||
|
||||
apply_reward: function(reward){
|
||||
var client = this.get('client');
|
||||
if (!client) {
|
||||
return;
|
||||
} else if (reward.type === 'gift') {
|
||||
var product = this.pos.db.get_product_by_id(reward.gift_product_id[0]);
|
||||
if (!product) {
|
||||
this.pos.pos_widget.screen_selector.show_popup('error',{
|
||||
'message':'Configuration Error',
|
||||
'comment':'The product associated with the reward "'+reward.name+'" could not be found. Make sure it is available for sale in the point of sale.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var line = this.addProduct(product, {
|
||||
price: 0,
|
||||
quantity: 1,
|
||||
merge: false,
|
||||
extras: { reward: reward },
|
||||
});
|
||||
|
||||
} else if (reward.type === 'discount') {
|
||||
|
||||
var lrounding = this.pos.loyalty.rounding;
|
||||
var crounding = this.pos.currency.rounding;
|
||||
var spendable = this.get_spendable_points();
|
||||
var order_total = this.getTotalTaxIncluded();
|
||||
var discount = round_pr(order_total * reward.discount,crounding);
|
||||
|
||||
if ( round_pr(discount * reward.point_cost,lrounding) > spendable ) {
|
||||
discount = round_pr(Math.floor( spendable / reward.point_cost ), crounding);
|
||||
}
|
||||
|
||||
var product = this.pos.db.get_product_by_id(reward.discount_product_id[0]);
|
||||
if (!product) {
|
||||
this.pos.pos_widget.screen_selector.show_popup('error',{
|
||||
'message':'Configuration Error',
|
||||
'comment':'The product associated with the reward "'+reward.name+'" could not be found. Make sure it is available for sale in the point of sale.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var line = this.addProduct(product, {
|
||||
price: -discount,
|
||||
quantity: 1,
|
||||
merge: false,
|
||||
extras: { reward: reward },
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
validate: function(){
|
||||
var client = this.get('client');
|
||||
if ( client ) {
|
||||
client.loyalty_points = this.get_new_total_points();
|
||||
}
|
||||
_super.prototype.validate.apply(this,arguments);
|
||||
},
|
||||
export_for_printing: function(){
|
||||
var json = _super.prototype.export_for_printing.apply(this,arguments);
|
||||
json.loyalty_points = this.get_new_points();
|
||||
return json;
|
||||
},
|
||||
export_as_JSON: function(){
|
||||
var json = _super.prototype.export_as_JSON.apply(this,arguments);
|
||||
json.loyalty_points = this.get_new_points();
|
||||
return json;
|
||||
},
|
||||
});
|
||||
|
||||
module.PosWidget.include({
|
||||
loyalty_reward_click: function(){
|
||||
var self = this;
|
||||
var order = this.pos.get('selectedOrder');
|
||||
var client = order.get('client');
|
||||
if (!client) {
|
||||
this.screen_selector.set_current_screen('clientlist');
|
||||
return;
|
||||
}
|
||||
|
||||
var rewards = order.get_available_rewards();
|
||||
if (rewards.length === 0) {
|
||||
this.screen_selector.show_popup('error',{
|
||||
'message': 'No Rewards Available',
|
||||
'comment': 'There are no rewards available for this customer as part of the loyalty program',
|
||||
});
|
||||
return;
|
||||
} else if (rewards.length === 1) {
|
||||
order.apply_reward(rewards[0]);
|
||||
return;
|
||||
} else {
|
||||
var list = [];
|
||||
for (var i = 0; i < rewards.length; i++) {
|
||||
list.push({
|
||||
label: rewards[i].name,
|
||||
item: rewards[i],
|
||||
});
|
||||
}
|
||||
this.screen_selector.show_popup('selection',{
|
||||
'message': 'Please select a reward',
|
||||
'list': list,
|
||||
'confirm': function(reward){
|
||||
order.apply_reward(reward);
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
build_widgets: function(){
|
||||
var self = this;
|
||||
this._super();
|
||||
|
||||
if(this.pos.loyalty && this.pos.loyalty.rewards.length ){
|
||||
var button = $(QWeb.render('LoyaltyButton'));
|
||||
button.click(function(){ self.loyalty_reward_click(); });
|
||||
button.appendTo(this.$('.control-buttons'));
|
||||
this.$('.control-buttons').removeClass('oe_hidden');
|
||||
}
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
module.OrderWidget.include({
|
||||
update_summary: function(){
|
||||
this._super();
|
||||
|
||||
var order = this.pos.get_order();
|
||||
|
||||
var $loypoints = $(this.el).find('.summary .loyalty-points');
|
||||
|
||||
if(this.pos.loyalty && order.get_client()){
|
||||
var points_won = order.get_won_points();
|
||||
var points_spent = order.get_spent_points();
|
||||
var points_total = order.get_new_total_points();
|
||||
$loypoints.replaceWith($(QWeb.render('LoyaltyPoints',{
|
||||
widget: this,
|
||||
rounding: this.pos.loyalty.rounding,
|
||||
points_won: points_won,
|
||||
points_spent: points_spent,
|
||||
points_total: points_total,
|
||||
})));
|
||||
$loypoints = $(this.el).find('.summary .loyalty-points');
|
||||
$loypoints.removeClass('oe_hidden');
|
||||
|
||||
if(points_total < 0){
|
||||
$loypoints.addClass('negative');
|
||||
}else{
|
||||
$loypoints.removeClass('negative');
|
||||
}
|
||||
}else{
|
||||
$loypoints.empty();
|
||||
$loypoints.addClass('oe_hidden');
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="LoyaltyPoints">
|
||||
<div class='loyalty-points'>
|
||||
<div class='loyalty-points-title'>Points</div>
|
||||
<t t-if='points_won'>
|
||||
<div class="loyalty-points-won">
|
||||
<span class='value'>+<t t-esc='widget.format_pr(points_won,rounding)'/></span>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if='points_spent'>
|
||||
<div class="loyalty-points-spent">
|
||||
<span class='value'>-<t t-esc='widget.format_pr(points_spent,rounding)'/></span>
|
||||
</div>
|
||||
</t>
|
||||
<div class='loyalty-points-total'>
|
||||
<span class='value'><t t-esc='widget.format_pr(points_total,rounding)' /></span>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="LoyaltyButton">
|
||||
<div class='control-button'>
|
||||
<i class='fa fa-star' /> Rewards
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-extend='OrderWidget'>
|
||||
<t t-jquery='.summary' t-operation='prepend'>
|
||||
<div class='loyalty-points oe_hidden'>.</div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-extend='ClientLine'>
|
||||
<t t-jquery='.client-line' t-operation='append'>
|
||||
<td>
|
||||
<t t-esc='widget.format_pr(partner.loyalty_points || 0, widget.pos.loyalty ? widget.pos.loyalty.rounding : 1.0)' />
|
||||
</td>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-extend='ClientDetails'>
|
||||
<t t-jquery='.client-details-right' t-operation='prepend'>
|
||||
<div class='client-detail'>
|
||||
<span class='label'>Points</span>
|
||||
<span class='detail client-points'>
|
||||
<t t-esc='widget.format_pr(partner.loyalty_points || 0, widget.pos.loyalty ? widget.pos.loyalty.rounding : 1.0)' />
|
||||
</span>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-extend='ClientListScreenWidget'>
|
||||
<t t-jquery='.client-list thead tr' t-operation='append'>
|
||||
<th>Points</th>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<template id="assets_frontend" inherit_id="web.assets_common">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/pos_loyalty/static/src/js/loyalty.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="css_pos" inherit_id="point_of_sale.index">
|
||||
<xpath expr="//head" position="inside">
|
||||
<link rel="stylesheet" href="/pos_loyalty/static/src/css/loyalty.css" />
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -0,0 +1,167 @@
|
||||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_config_form">
|
||||
<field name="name">pos.config.form.view</field>
|
||||
<field name="model">pos.config</field>
|
||||
<field name="inherit_id" ref="point_of_sale.view_pos_config_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//group[@string='Receipt']" position="after">
|
||||
<group string="Loyalty Program" col="4" >
|
||||
<field name="loyalty_id" />
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_partner_property_form">
|
||||
<field name="name">res.partner.product.property.form.inherit</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="point_of_sale.view_partner_property_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@string='Point of Sale']/group" position="after">
|
||||
<group name="loyalty" string="Loyalty Programs">
|
||||
<field name="loyalty_points" />
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_pos_form">
|
||||
<field name="name">pos.order.inherit</field>
|
||||
<field name="model">pos.order</field>
|
||||
<field name="inherit_id" ref="point_of_sale.view_pos_pos_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@string='Extra Info']" position="inside">
|
||||
<group name="loyalty" string="Loyalty Programs">
|
||||
<field name="loyalty_points" />
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_loyalty_program_form">
|
||||
<field name="name">loyalty.program.form.view</field>
|
||||
<field name="model">loyalty.program</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Loyalty Program">
|
||||
<sheet>
|
||||
|
||||
<div class="oe_title">
|
||||
<label class="oe_edit_only" for="name" string="Product Name"/>
|
||||
<h1><field name="name" class="oe_inline"/></h1>
|
||||
</div>
|
||||
|
||||
<group col="4">
|
||||
<field name="pp_currency" />
|
||||
<field name="pp_product" />
|
||||
<field name="pp_order" />
|
||||
<field name="rounding" />
|
||||
</group>
|
||||
|
||||
<separator string="Rules" colspan="4"/>
|
||||
<p>Rules change how loyalty points are earned for specific products</p>
|
||||
<field name="rule_ids" colspan="4" nolabel="1">
|
||||
<tree string="Rules">
|
||||
<field name="name" />
|
||||
<field name="product_id" />
|
||||
<field name="pp_product" />
|
||||
<field name="pp_currency" />
|
||||
<field name="override" />
|
||||
</tree>
|
||||
</field>
|
||||
|
||||
<separator string="Rewards" colspan="4"/>
|
||||
<p>Reward the customer with gifts or discounts for loyalty points</p>
|
||||
<field name="reward_ids" colspan="4" nolabel="1">
|
||||
<tree string="Rewards">
|
||||
<field name="name"/>
|
||||
<field name="type"/>
|
||||
</tree>
|
||||
</field>
|
||||
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_loyalty_rule_form">
|
||||
<field name="name">loyalty.rule.form.view</field>
|
||||
<field name="model">loyalty.rule</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Loyalty Rule">
|
||||
<div class="oe_title">
|
||||
<label class="oe_edit_only" for="name" string="Rule Name"/>
|
||||
<h1><field name="name" class="oe_inline"/></h1>
|
||||
</div>
|
||||
<group col="4">
|
||||
<field name="product_id" />
|
||||
<field name="pp_product" />
|
||||
<field name="pp_currency" />
|
||||
<field name="override" />
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_loyalty_reward_form">
|
||||
<field name="name">loyalty.reward.form.view</field>
|
||||
<field name="model">loyalty.reward</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Loyalty Reward">
|
||||
<div class="oe_title">
|
||||
<label class="oe_edit_only" for="name" string="Reward Name"/>
|
||||
<h1><field name="name" class="oe_inline"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<field name="type" />
|
||||
</group>
|
||||
<group >
|
||||
<field name="gift_product_id" attrs="{ 'invisible':[('type','!=','gift')], 'required':[('type','==','gift')]}" />
|
||||
<field name="discount" attrs="{ 'invisible':[('type','!=','discount')] }"/>
|
||||
<field name="discount_product_id" attrs="{ 'invisible':[('type','!=','discount')], 'required':[('type','==','discount')] }"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="minimum_points" />
|
||||
<field name="point_cost" />
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_loyalty_program_tree">
|
||||
<field name="name">loyalty.program.form.view</field>
|
||||
<field name="model">loyalty.program</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Loyalty Programs">
|
||||
<field name="name" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_loyalty_program_form">
|
||||
<field name="name">Loyalty Programs</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">loyalty.program</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to create a Loyalty Program.
|
||||
</p><p>
|
||||
Loyalty Programs allows you customer to earn points
|
||||
and rewards when doing business at your shops.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
parent="point_of_sale.menu_point_config_product"
|
||||
action="action_loyalty_program_form"
|
||||
id="menu_loyalty_program"
|
||||
sequence="30"
|
||||
groups="point_of_sale.group_pos_manager" />
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
Reference in New Issue
Block a user