[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:
Frederic van der Essen
2014-11-25 19:16:22 +01:00
committed by Frédéric van der Essen
parent f2fdaa017c
commit ee28165972
13 changed files with 900 additions and 0 deletions
@@ -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">
+25
View File
@@ -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:
+49
View File
@@ -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:
+132
View File
@@ -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;
}
+337
View File
@@ -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>
+18
View File
@@ -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>
+167
View File
@@ -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>