feat: add reward_claim_limit_type configuration to loyalty programs to toggle claim quantity restrictions in POS

This commit is contained in:
Suherdy Yacob 2026-06-17 21:47:11 +07:00
parent df0f853a68
commit 715d322b47
6 changed files with 46 additions and 1 deletions

View File

@ -1 +1,3 @@
# -*- coding: utf-8 -*-
from . import models

View File

@ -20,7 +20,9 @@ Use Case:
Ideal for retail environments running promotions such as 'Redeem X points for 1 Free Item'. By default, if a user has 3x points, Odoo allows them to claim 3 free items at once. This module strictly restricts the reward so only 1 free item can be claimed per transaction, exactly matching the configuration in the backend.
""",
'depends': ['point_of_sale', 'pos_loyalty'],
'data': [],
'data': [
'views/loyalty_program_views.xml',
],
'assets': {
'point_of_sale._assets_pos': [
'pos_loyalty_reward_qty_limit/static/src/app/models/pos_order.js',

3
models/__init__.py Normal file
View File

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import loyalty_program

17
models/loyalty_program.py Normal file
View File

@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
from odoo import models, fields
class LoyaltyProgram(models.Model):
_inherit = 'loyalty.program'
reward_claim_limit_type = fields.Selection([
('limited', 'Limited to 1 Time Claim'),
('multiple', 'Multiple Times Claim')
], string="Reward Claim Limit Type", default='limited', required=True,
help="If 'Limited to 1 Time Claim', the reward is capped at its defined quantity. If 'Multiple Times Claim', customers can claim the reward as many times as their points allow.")
def _load_pos_data_fields(self, config):
params = super()._load_pos_data_fields(config)
params.append('reward_claim_limit_type')
return params

View File

@ -7,6 +7,10 @@ patch(PosOrder.prototype, {
_computeUnclaimedFreeProductQty(reward, coupon_id, product, remainingPoints) {
const unclaimed = super._computeUnclaimedFreeProductQty(...arguments);
if (reward.program_id && reward.program_id.reward_claim_limit_type === 'multiple') {
return unclaimed;
}
// Find how many of this reward have already been claimed in the order
let claimed = 0;
for (const line of this.getOrderlines()) {
@ -26,6 +30,10 @@ patch(PosOrder.prototype, {
_computePotentialFreeProductQty(reward, product, remainingPoints) {
const potential = super._computePotentialFreeProductQty(...arguments);
if (reward.program_id && reward.program_id.reward_claim_limit_type === 'multiple') {
return potential;
}
// Apply the same cap here for display purposes
let claimed = 0;
for (const line of this.getOrderlines()) {

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="loyalty_program_view_form_inherit_reward_limit" model="ir.ui.view">
<field name="name">loyalty.program.view.form.inherit.reward.limit</field>
<field name="model">loyalty.program</field>
<field name="inherit_id" ref="loyalty.loyalty_program_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='applies_on'][not(@invisible='1')]" position="after">
<field name="reward_claim_limit_type"/>
</xpath>
</field>
</record>
</odoo>