feat: patch PosOrder to implement multi-level membership program selection logic
This commit is contained in:
parent
9768a3795c
commit
af99678406
@ -0,0 +1,91 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { PosOrder } from "@point_of_sale/app/models/pos_order";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
/**
|
||||
* Safely extract a numeric ID from a Many2One field value.
|
||||
* Handles three forms Odoo may return:
|
||||
* - raw integer: 5
|
||||
* - [id, name] tuple: [5, "Gold"]
|
||||
* - record object: { id: 5, name: "Gold", ... }
|
||||
*/
|
||||
function resolveManyToOneId(value) {
|
||||
if (!value && value !== 0) {
|
||||
return null;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return parseInt(value[0], 10);
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
return parseInt(value.id, 10);
|
||||
}
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
|
||||
// Track recursion depth to prevent infinite loops when filtering multiLevelPrograms
|
||||
let _checkingMultiLevel = false;
|
||||
|
||||
patch(PosOrder.prototype, {
|
||||
_programIsApplicable(program) {
|
||||
// Evaluate base program applicability first.
|
||||
const isApplicable = super._programIsApplicable(...arguments);
|
||||
if (!isApplicable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Custom Logic for Multi-Level Membership.
|
||||
// Guard against recursive calls triggered when filtering all programs below.
|
||||
if (program.multi_level_membership && !_checkingMultiLevel) {
|
||||
// Retrieve all loyalty programs
|
||||
const allPrograms = this.models['loyalty.program'].getAll();
|
||||
|
||||
// Set guard BEFORE filtering to prevent re-entry into this block
|
||||
_checkingMultiLevel = true;
|
||||
let multiLevelPrograms;
|
||||
try {
|
||||
// Filter programs that have the multi-level flag and pass the base applicability check.
|
||||
// With the guard set, this._programIsApplicable(p) will skip the custom block,
|
||||
// effectively calling only the base logic for each candidate program.
|
||||
multiLevelPrograms = allPrograms.filter(
|
||||
(p) => p.multi_level_membership && this._programIsApplicable(p)
|
||||
);
|
||||
} finally {
|
||||
_checkingMultiLevel = false;
|
||||
}
|
||||
|
||||
// If there are no applicable multi-level programs, block all of them.
|
||||
if (multiLevelPrograms.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let bestProgram = null;
|
||||
const partner = this.getPartner();
|
||||
|
||||
// If the partner is set and has a membership level, try to match it.
|
||||
if (partner && partner.membership_level_id) {
|
||||
const membershipId = resolveManyToOneId(partner.membership_level_id);
|
||||
if (membershipId) {
|
||||
// Find the matching program among applicable multi-level programs
|
||||
bestProgram = multiLevelPrograms.find((p) => p.id === membershipId) || null;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: pick the program with the smallest minimum_spend
|
||||
if (!bestProgram) {
|
||||
bestProgram = multiLevelPrograms.reduce((prev, curr) => {
|
||||
const prevSpend = prev.minimum_spend || 0;
|
||||
const currSpend = curr.minimum_spend || 0;
|
||||
return currSpend < prevSpend ? curr : prev;
|
||||
}, multiLevelPrograms[0]);
|
||||
}
|
||||
|
||||
// Only permit the chosen program to be active
|
||||
if (program.id !== bestProgram.id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user