-
- {{ __('Payment for ') }} {{ type }}:
+
+
+
+
+ {{ __('Payment for ') }} {{ type }}:
+
+
+ {{ orderSummary.data.title }}
+
-
- {{ orderSummary.data.title }}
-
-
-
-
- {{ __('Original Amount') }}
-
-
- {{ orderSummary.data.original_amount_formatted }}
-
-
-
-
{{ __('Discount') }}
-
-{{ orderSummary.data.discount_amount_formatted }}
-
-
-
- {{ __('GST Amount') }}
-
-
- {{ orderSummary.data.gst_amount_formatted }}
-
-
-
-
- {{ __('Total') }}
-
-
- {{ orderSummary.data.total_amount_formatted }}
-
-
-
-
{{
- __('Coupon')
- }}
+
+ {{ __('Original Amount') }}:
+
+
+ {{ orderSummary.data.original_amount_formatted }}
+
+
+
+
{{ __('Discount') }}:
+
- {{ orderSummary.data.discount_amount_formatted }}
+
+
+
+ {{ __('GST Amount') }}:
+
+
+ {{ orderSummary.data.gst_amount_formatted }}
+
+
+
+
+ {{ __('Total') }}:
+
+
+ {{ orderSummary.data.total_amount_formatted }}
+
+
+
+
+
+
+ {{ __('Enter a Coupon Code') }}:
+
+
+ {{ __('Apply') }}
+
+ variant="outline"
+ >
+
+
+
+
@@ -148,7 +146,7 @@
/>
@@ -193,6 +191,7 @@ import {
Breadcrumbs,
usePageMeta,
toast,
+ call,
} from 'frappe-ui'
import { reactive, inject, onMounted, computed, ref } from 'vue'
import { sessionStore } from '../stores/session'
@@ -238,10 +237,12 @@ const access = createResource({
const orderSummary = createResource({
url: 'lms.lms.utils.get_order_summary',
makeParams(values) {
+ console.log(appliedCoupon.value)
return {
doctype: props.type == 'batch' ? 'LMS Batch' : 'LMS Course',
docname: props.name,
country: billingDetails.country,
+ coupon: appliedCoupon.value,
}
},
onError(err) {
@@ -249,29 +250,7 @@ const orderSummary = createResource({
},
})
-const couponCode = ref('')
const appliedCoupon = ref(null)
-
-const applyCoupon = createResource({
- url: 'lms.lms.utils.apply_coupon',
- makeParams() {
- return {
- doctype: props.type == 'batch' ? 'LMS Batch' : 'LMS Course',
- docname: props.name,
- code: couponCode.value,
- country: billingDetails.country,
- }
- },
- onSuccess(data) {
- orderSummary.data = data
- appliedCoupon.value = couponCode.value
- toast.success(__('Coupon applied'))
- },
- onError(err) {
- toast.error(err.messages?.[0] || err)
- },
-})
-
const billingDetails = reactive({})
const setBillingDetails = (data) => {
@@ -329,13 +308,15 @@ const generatePaymentLink = () => {
}
function applyCouponCode() {
- if (!couponCode.value) return
- applyCoupon.submit()
+ if (!appliedCoupon.value) {
+ toast.error(__('Please enter a coupon code'))
+ return
+ }
+ orderSummary.reload()
}
function removeCoupon() {
appliedCoupon.value = null
- couponCode.value = ''
orderSummary.reload()
}
diff --git a/lms/lms/utils.py b/lms/lms/utils.py
index 927079fc..070c0183 100644
--- a/lms/lms/utils.py
+++ b/lms/lms/utils.py
@@ -953,73 +953,6 @@ def get_current_exchange_rate(source, target="USD"):
return details["rates"][target]
-@frappe.whitelist()
-def apply_coupon(doctype, docname, code, country=None):
- if doctype not in ["LMS Course", "LMS Batch"]:
- frappe.throw(_("Invalid doctype for coupon application."))
-
- if not code:
- frappe.throw(_("Coupon code is required."))
-
- summary = get_order_summary(doctype, docname, country)
-
- base_amount = summary.original_amount
- currency = summary.currency
-
- coupon_name = frappe.db.get_value("LMS Coupon", {"code": code.strip().upper(), "active": 1}, "name")
- if not coupon_name:
- frappe.throw(_("Invalid or inactive coupon code."))
-
- coupon = frappe.get_doc("LMS Coupon", coupon_name)
-
- if coupon.expires_on and getdate(coupon.expires_on) < getdate():
- frappe.throw(_("This coupon has expired."))
-
- if coupon.usage_limit and cint(coupon.times_redeemed) >= cint(coupon.usage_limit):
- frappe.throw(_("This coupon has reached its usage limit."))
-
- # check applicability
- applicable = True
- if len(coupon.applicable_items):
- applicable = any(
- (row.reference_doctype == doctype and row.reference_name == docname)
- for row in coupon.applicable_items
- )
- if not applicable:
- frappe.throw(_("This coupon is not applicable to this item."))
-
- discount_amount = 0
- if coupon.discount_type == "Percent":
- discount_amount = cint(flt(base_amount) * flt(coupon.percent_off) / 100)
- else:
- discount_amount = min(flt(coupon.amount_off), flt(base_amount))
-
- subtotal = max(flt(base_amount) - flt(discount_amount), 0)
-
- gst_applied = 0
- final_amount = subtotal
- if currency == "INR":
- final_amount, gst_applied = apply_gst(subtotal, country)
-
- return {
- "title": summary.title,
- "name": summary.name,
- "currency": currency,
- "original_amount": base_amount,
- "original_amount_formatted": fmt_money(base_amount, 0, currency),
- "discount_amount": discount_amount,
- "discount_amount_formatted": fmt_money(discount_amount, 0, currency),
- "amount": final_amount,
- "gst_applied": gst_applied,
- "gst_amount_formatted": fmt_money(gst_applied, 0, currency) if gst_applied else None,
- "total_amount_formatted": fmt_money(final_amount, 0, currency),
- "coupon": coupon.name,
- "coupon_code": coupon.code,
- "discount_type": coupon.discount_type,
- "discount_percent": coupon.percent_off if coupon.discount_type == "Percent" else None,
- }
-
-
@frappe.whitelist()
def change_currency(amount, currency, country=None):
amount = cint(amount)
@@ -1819,7 +1752,7 @@ def get_discussion_replies(topic):
@frappe.whitelist()
-def get_order_summary(doctype, docname, country=None):
+def get_order_summary(doctype, docname, coupon=None, country=None):
if doctype == "LMS Course":
details = frappe.db.get_value(
"LMS Course",
@@ -1856,6 +1789,12 @@ def get_order_summary(doctype, docname, country=None):
details.original_amount = details.amount
details.original_amount_formatted = fmt_money(details.amount, 0, details.currency)
+ if coupon:
+ discount_amount, subtotal = apply_coupon(doctype, docname, coupon, details.amount)
+ details.amount = subtotal
+ details.discount_amount = discount_amount
+ details.discount_amount_formatted = fmt_money(discount_amount, 0, details.currency)
+
if details.currency == "INR":
details.amount, details.gst_applied = apply_gst(details.amount, country)
details.gst_amount_formatted = fmt_money(details.gst_applied, 0, details.currency)
@@ -1864,6 +1803,74 @@ def get_order_summary(doctype, docname, country=None):
return details
+def apply_coupon(doctype, docname, code, base_amount):
+ coupon_name = frappe.db.exists("LMS Coupon", {"code": code, "enabled": 1})
+
+ if not coupon_name:
+ frappe.throw(_("Invalid or inactive coupon code."))
+
+ coupon = frappe.db.get_value(
+ "LMS Coupon",
+ coupon_name,
+ [
+ "expires_on",
+ "usage_limit",
+ "redemption_count",
+ "discount_type",
+ "percentage_discount",
+ "fixed_amount_discount",
+ "name",
+ "code",
+ ],
+ as_dict=True,
+ )
+
+ validate_coupon(doctype, code, coupon)
+ validate_coupon_applicability(doctype, docname, coupon_name)
+
+ discount_amount = calculate_discount_amount(base_amount, coupon)
+ subtotal = max(flt(base_amount) - flt(discount_amount), 0)
+
+ return discount_amount, subtotal
+
+
+def validate_coupon(doctype, code, coupon):
+ if doctype not in ["LMS Course", "LMS Batch"]:
+ frappe.throw(_("Invalid doctype for coupon application."))
+
+ if not code:
+ frappe.throw(_("Coupon code is required."))
+
+ if coupon.expires_on and getdate(coupon.expires_on) < getdate():
+ frappe.throw(_("This coupon has expired."))
+
+ if coupon.usage_limit and cint(coupon.redemption_count) >= cint(coupon.usage_limit):
+ frappe.throw(_("This coupon has reached its maximum usage limit."))
+
+
+def validate_coupon_applicability(doctype, docname, coupon_name):
+ applicable_item = frappe.db.exists(
+ "LMS Coupon Item", {"parent": coupon_name, "reference_doctype": doctype, "reference_name": docname}
+ )
+ if not applicable_item:
+ frappe.throw(
+ _("This coupon is not applicable to this {0}.").format(
+ "Course" if doctype == "LMS Course" else "Batch"
+ )
+ )
+
+
+def calculate_discount_amount(base_amount, coupon):
+ discount_amount = 0
+
+ if coupon.discount_type == "Percentage":
+ discount_amount = (base_amount * coupon.percentage_discount) / 100
+ elif coupon.discount_type == "Fixed Amount":
+ discount_amount = base_amount - coupon.fixed_amount_discount
+
+ return discount_amount
+
+
@frappe.whitelist()
def get_lesson_creation_details(course, chapter, lesson):
chapter_name = frappe.db.get_value("Chapter Reference", {"parent": course, "idx": chapter}, "chapter")