initial commit
This commit is contained in:
commit
ec65830d33
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
__pycache__
|
||||||
1
__init__.py
Normal file
1
__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
32
__manifest__.py
Normal file
32
__manifest__.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
{
|
||||||
|
'name': 'POS Loyalty Reward Quantity Limit',
|
||||||
|
'version': '1.0',
|
||||||
|
'category': 'Sales/Point of Sale',
|
||||||
|
'summary': 'Limits the maximum reward product quantity applied in POS to the value configured in the reward rule.',
|
||||||
|
'author': 'Abdul Aziz Amrullah',
|
||||||
|
'description': """
|
||||||
|
POS Loyalty Reward Quantity Limit
|
||||||
|
===================================
|
||||||
|
This module introduces a strict boundary on the total quantity of a reward product that can be applied within a single Point of Sale (POS) order.
|
||||||
|
|
||||||
|
Key Features:
|
||||||
|
- Replaces Odoo's default multiplier system for loyalty reward products.
|
||||||
|
- Enforces the 'Quantity Rewarded' (reward_product_qty) field on the Loyalty Reward rule as an absolute maximum limit.
|
||||||
|
- Ensures that regardless of how many points a customer has accumulated, they cannot claim more than the specified limit of the reward product in a single transaction.
|
||||||
|
- Automatically adjusts the deducted loyalty points to correctly match the capped quantity, preventing overcharging of points.
|
||||||
|
|
||||||
|
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': [],
|
||||||
|
'assets': {
|
||||||
|
'point_of_sale._assets_pos': [
|
||||||
|
'pos_loyalty_reward_qty_limit/static/src/app/models/pos_order.js',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'installable': True,
|
||||||
|
'application': False,
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
}
|
||||||
31
readme.md
Normal file
31
readme.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# POS Loyalty Reward Quantity Limit
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
This custom Odoo 19 module modifies the Point of Sale (POS) loyalty reward mechanism. It enforces a strict upper limit on the quantity of a reward product that can be applied to a single POS transaction.
|
||||||
|
|
||||||
|
By default, Odoo's loyalty engine allows customers with excess loyalty points to multiply a single reward rule, applying it multiple times to their cart in a single click. This module intervenes in this process by ensuring that the **"Quantity Rewarded" (`reward_product_qty`)** defined in the Loyalty Reward configuration acts as an **absolute maximum** per transaction.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- **Strict Limit Enforcement:** Prevents the system from giving out more free items than specified in the `reward_product_qty` field.
|
||||||
|
- **Accurate Point Deduction:** Automatically scales the required loyalty points deducted from the customer's wallet to match the capped quantity, ensuring no points are overcharged.
|
||||||
|
- **Dynamic UI Validation:** Updates the Point of Sale UI to dynamically hide or disable the reward button once the maximum quantity has been reached in the cart.
|
||||||
|
|
||||||
|
## Use Case Example
|
||||||
|
**Scenario:** A retail store runs a promotion where customers can redeem 100 points for **1** free coffee.
|
||||||
|
**Problem in standard Odoo:** If a customer has 300 points, clicking the reward will automatically add 3 free coffees to the cart and deduct 300 points.
|
||||||
|
**Solution with this module:** The module checks the "Quantity Rewarded" field (which is set to 1). When the customer clicks the reward, only **1** free coffee is added, and only 100 points are deducted. The reward button is then disabled for that transaction.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
1. Move the `pos_loyalty_reward_qty_limit` folder into your Odoo `custom/` addons directory.
|
||||||
|
2. Ensure you have the `point_of_sale` and `pos_loyalty` modules installed.
|
||||||
|
3. Turn on **Developer Mode**.
|
||||||
|
4. Go to **Apps** -> **Update Apps List**.
|
||||||
|
5. Search for `POS Loyalty Reward Quantity Limit` and install it.
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
This module patches the `PosOrder.prototype` located in the base `point_of_sale` frontend assets.
|
||||||
|
Specifically, it overrides:
|
||||||
|
- `_computeUnclaimedFreeProductQty`
|
||||||
|
- `_computePotentialFreeProductQty`
|
||||||
|
|
||||||
|
It dynamically calculates the `maxAllowed` available quantity by subtracting the already claimed amount from the configured `reward_product_qty`.
|
||||||
42
static/src/app/models/pos_order.js
Normal file
42
static/src/app/models/pos_order.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import { PosOrder } from "@point_of_sale/app/models/pos_order";
|
||||||
|
import { patch } from "@web/core/utils/patch";
|
||||||
|
|
||||||
|
patch(PosOrder.prototype, {
|
||||||
|
_computeUnclaimedFreeProductQty(reward, coupon_id, product, remainingPoints) {
|
||||||
|
const unclaimed = super._computeUnclaimedFreeProductQty(...arguments);
|
||||||
|
|
||||||
|
// Find how many of this reward have already been claimed in the order
|
||||||
|
let claimed = 0;
|
||||||
|
for (const line of this.getOrderlines()) {
|
||||||
|
if (line.reward_id && line.reward_id.id === reward.id) {
|
||||||
|
claimed += line.getQuantity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The maximum total quantity allowed for this reward in an order is reward_product_qty.
|
||||||
|
// So the maximum we can still claim is reward_product_qty - claimed.
|
||||||
|
let maxAllowed = reward.reward_product_qty - claimed;
|
||||||
|
if (maxAllowed < 0) maxAllowed = 0;
|
||||||
|
|
||||||
|
return Math.min(unclaimed, maxAllowed);
|
||||||
|
},
|
||||||
|
|
||||||
|
_computePotentialFreeProductQty(reward, product, remainingPoints) {
|
||||||
|
const potential = super._computePotentialFreeProductQty(...arguments);
|
||||||
|
|
||||||
|
// Apply the same cap here for display purposes
|
||||||
|
let claimed = 0;
|
||||||
|
for (const line of this.getOrderlines()) {
|
||||||
|
if (line.reward_id && line.reward_id.id === reward.id) {
|
||||||
|
claimed += line.getQuantity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let maxAllowed = reward.reward_product_qty - claimed;
|
||||||
|
if (maxAllowed < 0) maxAllowed = 0;
|
||||||
|
|
||||||
|
return Math.min(potential, maxAllowed);
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user