|
|
/* Copyright 2004-2010 OpenERP SA * Copyright 2017 RGB Consulting S.L. (https://www.rgbconsulting.com)
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
odoo.define('pos_loyalty.loyalty_program', function(require) { "use strict"
var models = require('point_of_sale.models'); var screens = require('point_of_sale.screens');
var utils = require('web.utils'); var round_pr = utils.round_precision;
var core = require('web.core'); var QWeb = core.qweb; var _t = core._t;
models.load_fields('res.partner', 'loyalty_points');
models.load_models([{ 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', 'cumulative', '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 = {}; self.loyalty.rules_by_category_id = {};
function update_rules(rules, rule, id) { if (!rules[id]) { rules[id] = [rule]; } else if (rule.cumulative) { rules[id].unshift(rule); } else { rules[id].push(rule); } }
_.each(rules, function(rule) { if (rule.type === 'product') update_rules(self.loyalty.rules_by_product_id, rule, rule.product_id[0]) else if (rule.type === 'category') update_rules(self.loyalty.rules_by_category_id, rule, rule.category_id[0]); }); }, }, { model: 'loyalty.reward', condition: function(self) { return !!self.loyalty; }, fields: ['name', 'type', 'minimum_points', 'gift_product_id', 'point_cost', 'discount_product_id', 'discount', 'discount_max', 'point_product_id'], domain: function(self) { return [ ['loyalty_program_id', '=', self.loyalty.id] ]; }, loaded: function(self, rewards) { self.loyalty.rewards = rewards; self.loyalty.rewards_by_id = {}; for (var i = 0; i < rewards.length; i++) { self.loyalty.rewards_by_id[rewards[i].id] = rewards[i]; } }, }, ], { 'after': 'product.product' });
var _orderline_super = models.Orderline.prototype; models.Orderline = models.Orderline.extend({ get_reward: function() { return this.pos.loyalty.rewards_by_id[this.reward_id]; }, set_reward: function(reward) { this.reward_id = reward.id; }, export_as_JSON: function() { var json = _orderline_super.export_as_JSON.apply(this, arguments); json.reward_id = this.reward_id; return json; }, init_from_JSON: function(json) { _orderline_super.init_from_JSON.apply(this, arguments); this.reward_id = json.reward_id; }, });
var _order_super = models.Order.prototype; models.Order = models.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(); 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.get_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 affected by a non cumulative rule, skip the others. (non cumulative rules are put
// at the beginning of the list when they are loaded )
if (!rule.cumulative) { overriden = true; break; } }
// Test the category rules
if (product.pos_categ_id) { var category = this.pos.db.get_category_by_id(product.pos_categ_id[0]); while (category && !overriden) { var rules = this.pos.loyalty.rules_by_category_id[category.id] || []; 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.cumulative) { overriden = true; break; } } var _category = category; category = this.pos.db.get_category_by_id(this.pos.db.get_category_parent_id(category.id)); if (_category === category) { 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(); var rounding = this.pos.loyalty.rounding; var points = 0;
for (var i = 0; i < lines.length; i++) { var line = lines[i]; var reward = line.get_reward(); if (reward) { if (reward.type === 'gift') { points += round_pr(line.get_quantity() * reward.point_cost, rounding); } else if (reward.type === 'discount') { points += reward.point_cost; } else if (reward.type === 'resale') { points += (-line.get_quantity()); } } }
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); } },
has_discount_reward: function() { var res = false; var lines = this.get_orderlines();
for (var i = 0; i < lines.length; i++) { var line = lines[i]; var reward = line.get_reward(); if (reward && reward.type === 'discount') { res = true; break; } } return res; },
/* The list of rewards that the current customer can get */ get_available_rewards: function() { var client = this.get_client(); if (!client) { return []; } var rewards = []; var discount_reward_set = this.has_discount_reward(); 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; } else if (reward.type === 'discount' && (discount_reward_set || 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) { return; }
this.add_product(product, { price: 0, quantity: 1, merge: false, extras: { reward_id: reward.id }, });
} else if (reward.type === 'discount') {
var crounding = this.pos.currency.rounding; var order_total = this.get_total_with_tax(); var discount = round_pr(order_total * reward.discount, crounding); var discount_max = reward.discount_max
if (discount_max && discount > discount_max) { discount = discount_max; }
var product = this.pos.db.get_product_by_id(reward.discount_product_id[0]);
if (!product) { return; }
this.add_product(product, { price: -discount, quantity: 1, merge: false, extras: { reward_id: reward.id }, });
} else if (reward.type === 'resale') {
var lrounding = this.pos.loyalty.rounding; var crounding = this.pos.currency.rounding; var spendable = this.get_spendable_points();
var order_total = this.get_total_with_tax(); var product = this.pos.db.get_product_by_id(reward.point_product_id[0]);
if (!product) { return; }
if (round_pr(spendable * product.price, crounding) > order_total) { spendable = round_pr(Math.floor(order_total / product.price), lrounding); }
if (spendable < 0.00001) { return; }
this.add_product(product, { quantity: -spendable, merge: false, extras: { reward_id: reward.id }, }); } },
finalize: function() { var client = this.get_client(); if (client) { client.loyalty_points = this.get_new_total_points(); this.pos.gui.screen_instances.clientlist.partner_cache.clear_node(client.id); } _order_super.finalize.apply(this, arguments); },
export_for_printing: function() { var json = _order_super.export_for_printing.apply(this, arguments); if (this.pos.loyalty && this.get_client()) { json.loyalty = { rounding: this.pos.loyalty.rounding || 1, name: this.pos.loyalty.name, client: this.get_client().name, points_won: this.get_won_points(), points_spent: this.get_spent_points(), points_total: this.get_new_total_points(), }; } return json; },
export_as_JSON: function() { var json = _order_super.export_as_JSON.apply(this, arguments); json.loyalty_points = this.get_new_points(); return json; }, });
var LoyaltyButton = screens.ActionButtonWidget.extend({ template: 'LoyaltyButton', button_click: function() { var self = this; var order = this.pos.get_order(); var client = order.get_client(); if (!client) { this.gui.show_screen('clientlist'); return; }
var rewards = order.get_available_rewards(); if (rewards.length === 0) { this.gui.show_popup('error', { 'title': _t('No Rewards Available'), 'body': _t('There are no rewards available for this customer as part of the loyalty program'), }); } else if (rewards.length === 1 && this.pos.loyalty.rewards.length === 1) { order.apply_reward(rewards[0]); } else { var list = []; for (var i = 0; i < rewards.length; i++) { list.push({ label: rewards[i].name, item: rewards[i], }); } this.gui.show_popup('selection', { 'title': _t('Please select a reward'), 'list': list, 'confirm': function(reward) { order.apply_reward(reward); }, }); } }, });
screens.define_action_button({ 'name': 'loyalty', 'widget': LoyaltyButton, 'condition': function() { return this.pos.loyalty && this.pos.loyalty.rewards.length; }, });
screens.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'); }
if (this.pos.loyalty && order.get_client() && this.getParent().action_buttons && this.getParent().action_buttons.loyalty) {
var rewards = order.get_available_rewards(); this.getParent().action_buttons.loyalty.highlight(!!rewards.length); } }, }); });
|