[FIX] pos_loyalty: prevent using expired eWallets

Current behavior:
It was possible to use an expired eWallet to pay for an order. It was
also possible to put money on an expired eWallet.

Steps to reproduce:
- Create an eWallet with an expiration date in the past
- Open PoS and add products to the order
- Click on the eWallet button
- You can still use the eWallet to pay for the order

opw-3568270

closes odoo/odoo#149622

X-original-commit: e3492b9192d9ca56104e0ce92371fffc1731b406
Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
Signed-off-by: Robin Engels (roen) <roen@odoo.com>
This commit is contained in:
roen-odoo
2024-01-17 18:12:44 +00:00
parent e1ed78511f
commit 5783e0573d
6 changed files with 93 additions and 8 deletions
+22
View File
@@ -331,6 +331,20 @@ msgstr ""
#. odoo-javascript
#: code:addons/pos_loyalty/static/src/app/control_buttons/reward_button/reward_button.js:0
#, python-format
msgid "No rewards available."
msgstr ""
#. module: pos_loyalty
#. odoo-javascript
#: code:addons/pos_loyalty/static/src/js/ControlButtons/eWalletButton.js:0
#, python-format
msgid "No valid eWallet found"
msgstr ""
#. module: pos_loyalty
#. odoo-javascript
#: code:addons/pos_loyalty/static/src/js/ControlButtons/RewardButton.js:0
#, python-format
msgid "Please select a product for this reward"
msgstr ""
@@ -754,6 +768,14 @@ msgstr ""
msgid "You cannot set negative quantity or price to gift card or ewallet."
msgstr ""
#. module: pos_loyalty
#. odoo-javascript
#: code:addons/pos_loyalty/static/src/js/ControlButtons/eWalletButton.js:0
#, python-format
msgid ""
"You either have not created an eWallet or all your eWallets have expired."
msgstr ""
#. module: pos_loyalty
#. odoo-python
#: code:addons/pos_loyalty/models/loyalty_program.py:0
@@ -18,10 +18,10 @@ export class eWalletButton extends Component {
_getEWalletRewards(order) {
const claimableRewards = order.getClaimableRewards();
const eWalletRewards = claimableRewards.filter(
({ reward }) => reward.program_id.program_type == "ewallet"
);
return eWalletRewards;
return claimableRewards.filter((reward_line) => {
const coupon = this.pos.couponCache[reward_line.coupon_id];
return coupon && reward_line.reward.program_id.program_type == 'ewallet' && !coupon.isExpired();
});
}
_getEWalletPrograms() {
return this.pos.programs.filter((p) => p.program_type == "ewallet");
@@ -31,6 +31,13 @@ export class eWalletButton extends Component {
const eWalletPrograms = this.pos.programs.filter((p) => p.program_type == "ewallet");
const orderTotal = order.get_total_with_tax();
const eWalletRewards = this._getEWalletRewards(order);
if (eWalletRewards.length === 0 && orderTotal >= 0) {
this.popup.add(ErrorPopup, {
title: _t('No valid eWallet found'),
body: _t('You either have not created an eWallet or all your eWallets have expired.'),
});
return;
}
if (orderTotal < 0 && eWalletPrograms.length >= 1) {
let selectedProgram = null;
if (eWalletPrograms.length == 1) {
@@ -25,13 +25,19 @@ export class PosLoyaltyCard {
* @param {number} program_id id of loyalty.program
* @param {number} partner_id id of res.partner
* @param {number} balance points on the coupon, not counting the order's changes
* @param {string} expiration_date
*/
constructor(code, id, program_id, partner_id, balance) {
constructor(code, id, program_id, partner_id, balance, expiration_date = false) {
this.code = code;
this.id = id || nextId--;
this.program_id = program_id;
this.partner_id = partner_id;
this.balance = balance;
this.expiration_date = expiration_date && new Date(expiration_date);
}
isExpired() {
return this.expiration_date && this.expiration_date < new Date();
}
}
@@ -1547,7 +1553,8 @@ patch(Order.prototype, {
payload.coupon_id,
payload.program_id,
payload.partner_id,
payload.points
payload.points,
payload.expiration_date,
);
this.pos.couponCache[coupon.id] = coupon;
this.codeActivatedCoupons.push(coupon);
@@ -309,7 +309,7 @@ patch(PosStore.prototype, {
const result = await this.env.services.orm.searchRead(
"loyalty.card",
domain,
["id", "points", "code", "partner_id", "program_id"],
["id", "points", "code", "partner_id", "program_id", "expiration_date"],
{ limit }
);
if (Object.keys(this.couponCache).length + result.length > COUPON_CACHE_MAX_SIZE) {
@@ -327,7 +327,8 @@ patch(PosStore.prototype, {
dbCoupon.id,
dbCoupon.program_id[0],
dbCoupon.partner_id[0],
dbCoupon.points
dbCoupon.points,
dbCoupon.expiration_date
);
this.couponCache[coupon.id] = coupon;
this.partnerId2CouponIds[coupon.partner_id] =
@@ -5,6 +5,7 @@ import * as ProductScreen from "@point_of_sale/../tests/tours/helpers/ProductScr
import * as TicketScreen from "@point_of_sale/../tests/tours/helpers/TicketScreenTourMethods";
import * as Chrome from "@point_of_sale/../tests/tours/helpers/ChromeTourMethods";
import * as PartnerListScreen from "@point_of_sale/../tests/tours/helpers/PartnerListScreenTourMethods";
import * as ErrorPopup from "@point_of_sale/../tests/tours/helpers/ErrorPopupTourMethods";
import { registry } from "@web/core/registry";
//#region EWalletProgramTour1
@@ -105,3 +106,21 @@ registry.category("web_tour.tours").add("EWalletProgramTour2", {
});
//#endregion
registry
.category("web_tour.tours")
.add('ExpiredEWalletProgramTour', {
test: true,
url: '/pos/ui',
steps: () => [
ProductScreen.confirmOpeningPopup(),
ProductScreen.clickHomeCategory(),
ProductScreen.clickPartnerButton(),
ProductScreen.clickCustomer('AAAA'),
ProductScreen.addOrderline('Whiteboard Pen', '2', '6', '12.00'),
PosLoyalty.eWalletButtonState({ highlighted: false }),
PosLoyalty.clickEWalletButton(),
ErrorPopup.isShown(),
ErrorPopup.clickConfirm()
].flat(),
});
+29
View File
@@ -1428,3 +1428,32 @@ class TestUi(TestPointOfSaleHttpCommon):
"PosLoyaltyTour9",
login="pos_user",
)
def test_ewallet_expiration_date(self):
"""
Test for ewallet program.
- Collect points in EWalletProgramTour1.
- Use points in EWalletProgramTour2.
"""
LoyaltyProgram = self.env['loyalty.program']
# Deactivate all other programs to avoid interference
(LoyaltyProgram.search([])).write({'pos_ok': False})
# But activate the ewallet_product_50 because it's shared among new ewallet programs.
self.env.ref('loyalty.ewallet_product_50').write({'active': True})
# Create ewallet program
ewallet_program = self.create_programs([('arbitrary_name', 'ewallet')])['arbitrary_name']
# Create test partners
partner_aaa = self.env['res.partner'].create({'name': 'AAAA'})
#Create an eWallet for partner_aaa
self.env['loyalty.card'].create({
'partner_id': partner_aaa.id,
'program_id': ewallet_program.id,
'points': 50,
'expiration_date': '2020-01-01',
})
self.main_pos_config.open_ui()
self.start_tour(
"/pos/web?config_id=%d" % self.main_pos_config.id,
"ExpiredEWalletProgramTour",
login="pos_user",
)