fix: override getClaimableRewards to correctly display product rewards for manual membership programs

This commit is contained in:
Suherdy Yacob 2026-06-04 11:23:00 +07:00
parent 1f85ab68bc
commit 1a1ad81a55

View File

@ -115,5 +115,76 @@ patch(PosOrder.prototype, {
return true;
},
/**
* Override to fix free product rewards for manual_membership (subscription) programs.
*
* Odoo's base _computeUnclaimedFreeProductQty returns Math.min(available, freeQty).
* `available` = qty of the reward product already in the current order.
* For subscription programs (e.g. Makan Pagi Gratis), the free breakfast product is
* NOT in the order yet so available=0, freeQty=0 and the reward is never shown.
*
* Fix: after the base getClaimableRewards runs, we re-evaluate manual_membership
* programs that were silently skipped and inject their product rewards using the
* raw point-based qty (points / required_points), so the cashier can select them
* from the Rewards popup and the product is added to the order automatically.
*/
getClaimableRewards(coupon_id = false, program_id = false, auto = false) {
const baseResult = super.getClaimableRewards(...arguments);
// Auto mode is the background reward sweep — don't alter it.
if (auto) {
return baseResult;
}
// Collect program IDs already present so we don't add duplicates.
const presentProgramIds = new Set(
baseResult.map((r) => r.reward?.program_id?.id)
);
const couponPointChanges = this.uiState.couponPointChanges;
const extraRewards = [];
for (const pe of Object.values(couponPointChanges)) {
const program = this.models['loyalty.program'].get(pe.program_id);
if (!program || !program.manual_membership) {
continue;
}
if (presentProgramIds.has(program.id)) {
continue;
}
if (coupon_id && pe.coupon_id !== coupon_id) {
continue;
}
if (program_id && pe.program_id !== program_id) {
continue;
}
const points = this._getRealCouponPoints(pe.coupon_id);
for (const reward of program.reward_ids) {
if (points < reward.required_points) {
continue;
}
if (reward.reward_type !== 'product') {
// Non-product rewards are already handled by base logic.
continue;
}
// Compute how many free items can be claimed from remaining points.
const freeQty = Math.floor(
(points / reward.required_points) * (reward.reward_product_qty || 1)
);
if (freeQty <= 0) {
continue;
}
extraRewards.push({
coupon_id: pe.coupon_id,
reward: reward,
potentialQty: freeQty,
});
}
}
return [...baseResult, ...extraRewards];
},
});