From 35d01765f9b925467b6ac20d2ff83c81f49e4d3e Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 2 Oct 2025 16:03:44 +0700 Subject: fix cannot apply voucher when cart has promotion item --- indoteknik_api/controllers/api_v1/sale_order.py | 10 +++- indoteknik_custom/models/sale_order.py | 65 ++++++++++++++++++++----- 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index 1a75c830..25268856 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -808,6 +808,14 @@ class SaleOrder(controller.Controller): sale_order.apply_promotion_program() sale_order.add_free_product(promotions) + # Pastikan baris hasil promo/bonus ditandai supaya bisa di-skip voucher + promo_lines = sale_order.order_line.filtered( + lambda l: getattr(l, 'order_promotion_id', False) or (l.price_unit or 0.0) == 0.0 + ) + if promo_lines: + promo_lines.write({'is_has_disc': True}) + _logger.info(f"[PROMO_MARK] Marked {len(promo_lines)} promo/free lines as is_has_disc=True") + voucher_code = params['value']['voucher'] if voucher_code: _logger.info(f"Processing voucher: {voucher_code}") @@ -816,7 +824,7 @@ class SaleOrder(controller.Controller): voucher_shipping = request.env['voucher'].search( [('code', '=', voucher_code), ('apply_type', 'in', ['shipping'])], limit=1) - if voucher and len(promotions) == 0: + if voucher: _logger.info("Applying regular voucher") sale_order.voucher_id = voucher.id sale_order.apply_voucher() diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 663cba58..3aaae12d 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2871,6 +2871,7 @@ class SaleOrder(models.Model): def action_apply_voucher(self): for line in self.order_line: if line.order_promotion_id: + _logger.warning(f"[CHECKOUT FAILED] Produk promo ditemukan: {line.product_id.display_name}") raise UserError('Voucher tidak dapat digabung dengan promotion program') voucher = self.voucher_id @@ -2913,42 +2914,82 @@ class SaleOrder(models.Model): self.apply_voucher_shipping() def apply_voucher(self): + def _is_promo_line(line): + # TRUE jika baris tidak boleh kena voucher + if getattr(line, 'order_promotion_id', False): + return True # baris dari program promo + if (line.price_unit or 0.0) == 0.0: + return True # free item + if getattr(line, 'is_has_disc', False): + return True # sudah promo/flashsale/berdiskon + if (line.discount or 0.0) >= 100.0: + return True # safety + return False + + # --- LOOP 1: susun input untuk voucher.apply() --- order_line = [] for line in self.order_line: + if _is_promo_line(line): + continue order_line.append({ 'product_id': line.product_id, 'price': line.price_unit, 'discount': line.discount, 'qty': line.product_uom_qty, - 'subtotal': line.price_subtotal + 'subtotal': line.price_subtotal, }) + + if not order_line: + return + voucher = self.voucher_id.apply(order_line) + # --- LOOP 2: tulis hasilnya HANYA ke non-promo --- for line in self.order_line: + if _is_promo_line(line): + continue + line.initial_discount = line.discount voucher_type = voucher['type'] - used_total = voucher['total'][voucher_type] - used_discount = voucher['discount'][voucher_type] + total_map = voucher['total'][voucher_type] + discount_map = voucher['discount'][voucher_type] - manufacture_id = line.product_id.x_manufacture.id if voucher_type == 'brand': - used_total = used_total.get(manufacture_id) - used_discount = used_discount.get(manufacture_id) + m_id = line.product_id.x_manufacture.id + used_total = (total_map or {}).get(m_id) + used_discount = (discount_map or {}).get(m_id) + else: + used_total = total_map + used_discount = discount_map - if not used_total or not used_discount: + if not used_total or not used_discount or (line.product_uom_qty or 0.0) == 0.0: continue line_contribution = line.price_subtotal / used_total line_voucher = used_discount * line_contribution - line_voucher_item = line_voucher / line.product_uom_qty + per_item_voucher = line_voucher / line.product_uom_qty - line_price_unit = line.price_unit / 1.11 if any(tax.id == 23 for tax in line.tax_id) else line.price_unit - line_discount_item = line_price_unit * line.discount / 100 + line_voucher_item - line_voucher_item = line_discount_item / line_price_unit * 100 + has_ppn_11 = any(tax.id == 23 for tax in line.tax_id) + base_unit = line.price_unit / 1.11 if has_ppn_11 else line.price_unit + + new_disc_value = base_unit * line.discount / 100 + per_item_voucher + new_disc_pct = (new_disc_value / base_unit) * 100 line.amount_voucher_disc = line_voucher - line.discount = line_voucher_item + line.discount = new_disc_pct + + _logger.info( + "[VOUCHER_APPLIED] SO=%s voucher=%s type=%s line_id=%s product=%s qty=%s discount_pct=%.2f amount_voucher=%s", + self.name, + getattr(self.voucher_id, "code", None), + voucher.get("type"), + line.id, + line.product_id.display_name, + line.product_uom_qty, + line.discount, + line.amount_voucher_disc, + ) self.amount_voucher_disc = voucher['discount']['all'] self.applied_voucher_id = self.voucher_id -- cgit v1.2.3