feat: implement subscription card reuse and prevent automatic ghost card creation in POS orders

This commit is contained in:
Suherdy Yacob 2026-06-10 10:06:52 +07:00
parent 4e39b450e4
commit fc32137185

View File

@ -67,15 +67,70 @@ class PosOrder(models.Model):
return res return res
def _check_existing_loyalty_cards(self, coupon_data):
"""Extend core to also match existing subscription cards so they are
reused (point-updated) rather than triggering new-card creation."""
# Run the standard matching first (covers 'loyalty' and 'ewallet').
super()._check_existing_loyalty_cards(coupon_data)
# Now handle 'subscription' type programs the same way.
coupon_key_to_modify = []
for coupon_id, coupon_vals in coupon_data.items():
partner_id = coupon_vals.get('partner_id', False)
if not partner_id:
continue
program = self.env['loyalty.program'].browse(coupon_vals['program_id']).exists()
if not program or program.program_type != 'subscription':
continue
existing = self.env['loyalty.card'].search([
('partner_id', '=', partner_id),
('program_id', '=', coupon_vals['program_id']),
], limit=1)
if existing:
coupon_vals['coupon_id'] = existing.id
coupon_key_to_modify.append([coupon_id, existing.id])
for old_key, new_key in coupon_key_to_modify:
coupon_data[new_key] = coupon_data.pop(old_key)
def confirm_coupon_programs(self, coupon_data): def confirm_coupon_programs(self, coupon_data):
"""Strip any to-be-created (negative-id) coupon_data entries that
target a subscription program before handing off to core.
Background: the POS JS adds a {points: 0} couponPointChange for every
is_nominative program (applies_on='both') whenever a partner is on the
order including Subscription Direksi. Core would then create a fresh
loyalty.card for that partner. Subscription cards must only be created
manually, so we intercept and remove these phantom entries here.
"""
coupon_data_int = {int(k): v for k, v in coupon_data.items()}
# Collect negative (new-card) ids that belong to subscription programs.
keys_to_remove = []
for coupon_id, coupon_vals in coupon_data_int.items():
if coupon_id >= 0:
# Positive id = existing card; let core handle it normally.
continue
program = self.env['loyalty.program'].browse(
coupon_vals.get('program_id')
).exists()
if program and program.program_type == 'subscription':
keys_to_remove.append(coupon_id)
# Remove the ghost entries (work on the original string-key dict).
for key in keys_to_remove:
coupon_data.pop(key, None)
coupon_data.pop(str(key), None)
# Run super to process normal workflow # Run super to process normal workflow
res = super().confirm_coupon_programs(coupon_data) res = super().confirm_coupon_programs(coupon_data)
# After points calculations/deductions are processed, reset the points back to 0.0 for subscription cards # After points calculations/deductions are processed, reset the points
# back to 0.0 for any subscription cards that were actually applied.
coupon_data_int = {int(k): v for k, v in coupon_data.items()} coupon_data_int = {int(k): v for k, v in coupon_data.items()}
for card_id in coupon_data_int.keys(): for card_id in coupon_data_int.keys():
card = self.env['loyalty.card'].browse(card_id).exists() card = self.env['loyalty.card'].browse(card_id).exists()
if card and card.program_id.program_type == 'subscription': if card and card.program_id.program_type == 'subscription':
card.points = 0.0 card.points = 0.0
return res return res