From 5310cc57c4fb50d75e6c4267fa625ab252a05d4e Mon Sep 17 00:00:00 2001 From: MT Date: Sat, 28 Mar 2026 16:29:18 +0200 Subject: [PATCH] fix: honor coupon apply_on in POS --- POS/src/components/sale/CouponDialog.vue | 28 +++++++++++++++++++----- POS/src/composables/useInvoice.js | 13 +++++++---- POS/src/pages/POSSale.vue | 2 ++ 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/POS/src/components/sale/CouponDialog.vue b/POS/src/components/sale/CouponDialog.vue index f6569100..e9494a1c 100644 --- a/POS/src/components/sale/CouponDialog.vue +++ b/POS/src/components/sale/CouponDialog.vue @@ -162,6 +162,14 @@ const props = defineProps({ required: true, note: __("Cart subtotal BEFORE tax - used for discount calculations"), }, + taxAmount: { + type: Number, + default: 0, + }, + grandTotal: { + type: Number, + default: 0, + }, items: Array, posProfile: String, customer: String, @@ -257,6 +265,14 @@ function applyGiftCard(card) { applyCoupon() } +function getCouponBaseAmount(coupon) { + const grandTotal = Number.parseFloat(props.grandTotal || 0) + const taxAmount = Number.parseFloat(props.taxAmount || 0) + const netTotal = Math.max(grandTotal - taxAmount, 0) + + return coupon.apply_on === "Grand Total" ? grandTotal : netTotal +} + async function applyCoupon() { if (!couponCode.value.trim()) { errorMessage.value = __("Please enter a coupon code") @@ -282,9 +298,10 @@ async function applyCoupon() { } const coupon = validationData.coupon + const baseAmount = getCouponBaseAmount(coupon) - // Check minimum amount (on subtotal before tax) - if (coupon.min_amount && props.subtotal < coupon.min_amount) { + // Check minimum amount on the configured coupon base + if (coupon.min_amount && baseAmount < coupon.min_amount) { errorMessage.value = __('This coupon requires a minimum purchase of ', [formatCurrency(coupon.min_amount)]) showWarning(errorMessage.value) return @@ -297,15 +314,15 @@ async function applyCoupon() { amount: coupon.discount_type === "Amount" ? coupon.discount_amount : 0, } - let discountAmount = calculateDiscountAmount(discountObj, props.subtotal) + let discountAmount = calculateDiscountAmount(discountObj, baseAmount) // Apply maximum discount limit if specified if (coupon.max_amount && discountAmount > coupon.max_amount) { discountAmount = coupon.max_amount } - // Clamp discount to subtotal to prevent negative totals - discountAmount = Math.min(discountAmount, props.subtotal) + // Clamp discount to the selected coupon base to prevent negative totals + discountAmount = Math.min(discountAmount, baseAmount) appliedDiscount.value = { name: coupon.coupon_name || coupon.coupon_code, @@ -315,6 +332,7 @@ async function applyCoupon() { type: coupon.discount_type, coupon: coupon, apply_on: coupon.apply_on, + base_amount: baseAmount, } emit("discount-applied", appliedDiscount.value) diff --git a/POS/src/composables/useInvoice.js b/POS/src/composables/useInvoice.js index 66c7c58f..88a3340a 100644 --- a/POS/src/composables/useInvoice.js +++ b/POS/src/composables/useInvoice.js @@ -534,12 +534,17 @@ export function useInvoice() { // Store coupon code for tracking couponCode.value = discount.code || discount.name + const baseAmount = + typeof discount.base_amount === "number" + ? discount.base_amount + : subtotal.value + // Use centralized calculation to handle percentage/amount and clamping - let discountAmount = calculateDiscountAmount(discount, subtotal.value) + let discountAmount = calculateDiscountAmount(discount, baseAmount) - // Clamp discount to subtotal (cannot exceed total) - if (discountAmount > subtotal.value) { - discountAmount = subtotal.value + // Clamp discount to the same base the coupon was calculated against + if (discountAmount > baseAmount) { + discountAmount = baseAmount } // Ensure non-negative diff --git a/POS/src/pages/POSSale.vue b/POS/src/pages/POSSale.vue index 9ac5819d..36725927 100644 --- a/POS/src/pages/POSSale.vue +++ b/POS/src/pages/POSSale.vue @@ -545,6 +545,8 @@