refactor: remove temporary coupon_id patching in favor of filtering orphaned reward lines in _get_reward_lines

This commit is contained in:
Suherdy Yacob 2026-06-01 13:02:33 +07:00
parent 4c01b4a5e0
commit 047f8a0987

View File

@ -74,25 +74,6 @@ patch(PosOrder.prototype, {
}); });
}, },
/**
* getLoyaltyPoints reads coupon_id.id guard undefined coupon_id.
*/
getLoyaltyPoints() {
const undefinedCouponLines = [];
for (const line of this._get_reward_lines()) {
if (!line.coupon_id) {
line.coupon_id = { id: 0 };
undefinedCouponLines.push(line);
}
}
try {
return super.getLoyaltyPoints(...arguments);
} finally {
for (const line of undefinedCouponLines) {
line.coupon_id = undefined;
}
}
},
/** /**
* _updateRewardLines purge orphans before core re-processes them. * _updateRewardLines purge orphans before core re-processes them.
@ -104,7 +85,7 @@ patch(PosOrder.prototype, {
/** /**
* _validForPointsCorrection core accesses reward.program_id.id without null check. * _validForPointsCorrection core accesses reward.program_id.id without null check.
* Guard it here. * Guard it here. This also guards the lines accessed in _getPointsCorrection.
*/ */
_validForPointsCorrection(reward, line, rule) { _validForPointsCorrection(reward, line, rule) {
if (!reward || !reward.program_id || !rule.program_id) { if (!reward || !reward.program_id || !rule.program_id) {
@ -113,28 +94,21 @@ patch(PosOrder.prototype, {
return super._validForPointsCorrection(...arguments); return super._validForPointsCorrection(...arguments);
}, },
_getPointsCorrection(program) { /**
if (!program || !program.rule_ids) { * _get_reward_lines exclude orphaned reward lines (no valid reward_id.program_id).
return 0; * This also protects _getPointsCorrection which iterates this.lines.filter(is_reward_line).
} * NOTE: Do NOT reassign this.lines that goes through the ORM setter and permanently
const originalLines = this.lines; * mutates/destroys lines. Instead, filter at the method level.
this.lines = this.lines.filter( */
(line) => !(line.is_reward_line && (!line.reward_id || !line.reward_id.program_id))
);
try {
return super._getPointsCorrection(...arguments);
} finally {
this.lines = originalLines;
}
},
_get_reward_lines() { _get_reward_lines() {
const rewardLines = super._get_reward_lines(...arguments); const rewardLines = super._get_reward_lines(...arguments);
if (!rewardLines) { if (!rewardLines) {
return []; return [];
} }
// Exclude orphaned lines with missing reward_id.program_id OR missing coupon_id.
// getLoyaltyPoints accesses line.coupon_id.id which crashes if coupon_id is undefined.
return rewardLines.filter( return rewardLines.filter(
(line) => line.reward_id && line.reward_id.program_id (line) => line.reward_id && line.reward_id.program_id && line.coupon_id
); );
}, },
@ -169,20 +143,12 @@ patch(PosOrder.prototype, {
patch(OrderPaymentValidation.prototype, { patch(OrderPaymentValidation.prototype, {
async validateOrder(isForceValidate) { async validateOrder(isForceValidate) {
const undefinedCouponLines = []; // Purge any orphaned reward lines before validation to prevent crashes
for (const line of this.order._get_reward_lines()) { // in confirm_coupon_programs when it iterates reward lines.
if (!line.coupon_id) { if (this.order) {
line.coupon_id = { id: 0 }; purgeOrphanedRewardLines(this.order);
undefinedCouponLines.push(line);
}
}
try {
return await super.validateOrder(...arguments);
} finally {
for (const line of undefinedCouponLines) {
line.coupon_id = undefined;
}
} }
return await super.validateOrder(...arguments);
}, },
}); });
@ -237,14 +203,6 @@ patch(PosStore.prototype, {
async postProcessLoyalty(order) { async postProcessLoyalty(order) {
purgeOrphanedRewardLines(order); purgeOrphanedRewardLines(order);
const undefinedCouponLines = [];
for (const line of order._get_reward_lines()) {
if (!line.coupon_id) {
line.coupon_id = { id: 0 };
undefinedCouponLines.push(line);
}
}
const originalCall = this.data.call; const originalCall = this.data.call;
this.data.call = function (model, method, args) { this.data.call = function (model, method, args) {
if (model === "pos.order" && method === "confirm_coupon_programs") { if (model === "pos.order" && method === "confirm_coupon_programs") {
@ -260,9 +218,6 @@ patch(PosStore.prototype, {
return await super.postProcessLoyalty(...arguments); return await super.postProcessLoyalty(...arguments);
} finally { } finally {
this.data.call = originalCall; this.data.call = originalCall;
for (const line of undefinedCouponLines) {
line.coupon_id = undefined;
}
} }
}, },
}); });