chore: applied pre-commit hooks
This commit is contained in:
@@ -2,75 +2,75 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import nowdate
|
||||
|
||||
|
||||
class LMSCoupon(Document):
|
||||
def validate(self):
|
||||
if self.code:
|
||||
self.code = self.code.strip().upper()
|
||||
def validate(self):
|
||||
if self.code:
|
||||
self.code = self.code.strip().upper()
|
||||
|
||||
if not self.code:
|
||||
frappe.throw(_("Coupon code is required"))
|
||||
if not self.code:
|
||||
frappe.throw(_("Coupon code is required"))
|
||||
|
||||
if len(self.code) < 6:
|
||||
frappe.throw(_("Coupon code must be atleast 6 characters"))
|
||||
if len(self.code) < 6:
|
||||
frappe.throw(_("Coupon code must be atleast 6 characters"))
|
||||
|
||||
if self.name:
|
||||
existing = frappe.db.exists(
|
||||
"LMS Coupon",
|
||||
{
|
||||
"code": self.code,
|
||||
"name": ["!=", self.name],
|
||||
},
|
||||
)
|
||||
else:
|
||||
existing = frappe.db.exists("LMS Coupon", {"code": self.code})
|
||||
|
||||
if existing:
|
||||
frappe.throw(_("Coupon code is already taken. Use a different one"))
|
||||
if self.name:
|
||||
existing = frappe.db.exists(
|
||||
"LMS Coupon",
|
||||
{
|
||||
"code": self.code,
|
||||
"name": ["!=", self.name],
|
||||
},
|
||||
)
|
||||
else:
|
||||
existing = frappe.db.exists("LMS Coupon", {"code": self.code})
|
||||
|
||||
if not self.discount_type:
|
||||
frappe.throw(_("Discount type is required"))
|
||||
if existing:
|
||||
frappe.throw(_("Coupon code is already taken. Use a different one"))
|
||||
|
||||
if self.discount_type == "Percent":
|
||||
if not self.percent_off or self.percent_off == "":
|
||||
frappe.throw(_("Discount percentage is required"))
|
||||
try:
|
||||
percent_value = float(self.percent_off)
|
||||
if not (0 < percent_value <= 100):
|
||||
frappe.throw(_("Discount percentage must be between 1 and 100"))
|
||||
except (ValueError, TypeError):
|
||||
frappe.throw(_("Discount percentage must be a valid number"))
|
||||
self.amount_off = None
|
||||
if not self.discount_type:
|
||||
frappe.throw(_("Discount type is required"))
|
||||
|
||||
if self.discount_type == "Amount":
|
||||
if not self.amount_off or self.amount_off == "":
|
||||
frappe.throw(_("Discount amount is required"))
|
||||
try:
|
||||
amount_value = float(self.amount_off)
|
||||
if amount_value < 0:
|
||||
frappe.throw(_("Discount amount cannot be negative"))
|
||||
except (ValueError, TypeError):
|
||||
frappe.throw(_("Discount amount must be a valid number"))
|
||||
self.percent_off = None
|
||||
if self.discount_type == "Percent":
|
||||
if not self.percent_off or self.percent_off == "":
|
||||
frappe.throw(_("Discount percentage is required"))
|
||||
try:
|
||||
percent_value = float(self.percent_off)
|
||||
if not (0 < percent_value <= 100):
|
||||
frappe.throw(_("Discount percentage must be between 1 and 100"))
|
||||
except (ValueError, TypeError):
|
||||
frappe.throw(_("Discount percentage must be a valid number"))
|
||||
self.amount_off = None
|
||||
|
||||
if self.usage_limit is not None and self.usage_limit != "":
|
||||
try:
|
||||
usage_value = int(self.usage_limit)
|
||||
if usage_value < 0:
|
||||
frappe.throw(_("Usage limit cannot be negative"))
|
||||
except (ValueError, TypeError):
|
||||
frappe.throw(_("Usage limit must be a valid number"))
|
||||
if self.discount_type == "Amount":
|
||||
if not self.amount_off or self.amount_off == "":
|
||||
frappe.throw(_("Discount amount is required"))
|
||||
try:
|
||||
amount_value = float(self.amount_off)
|
||||
if amount_value < 0:
|
||||
frappe.throw(_("Discount amount cannot be negative"))
|
||||
except (ValueError, TypeError):
|
||||
frappe.throw(_("Discount amount must be a valid number"))
|
||||
self.percent_off = None
|
||||
|
||||
if self.expires_on and str(self.expires_on) < nowdate():
|
||||
frappe.throw(_("Expiry date cannot be in the past"))
|
||||
if self.usage_limit is not None and self.usage_limit != "":
|
||||
try:
|
||||
usage_value = int(self.usage_limit)
|
||||
if usage_value < 0:
|
||||
frappe.throw(_("Usage limit cannot be negative"))
|
||||
except (ValueError, TypeError):
|
||||
frappe.throw(_("Usage limit must be a valid number"))
|
||||
|
||||
if not self.get("applicable_items") or len(self.get("applicable_items")) == 0:
|
||||
frappe.throw(_("Please select atleast one course or batch"))
|
||||
if self.expires_on and str(self.expires_on) < nowdate():
|
||||
frappe.throw(_("Expiry date cannot be in the past"))
|
||||
|
||||
for item in self.get("applicable_items"):
|
||||
if not item.get("reference_name"):
|
||||
frappe.throw(_("Please select a valid course or batch"))
|
||||
if not self.get("applicable_items") or len(self.get("applicable_items")) == 0:
|
||||
frappe.throw(_("Please select atleast one course or batch"))
|
||||
|
||||
for item in self.get("applicable_items"):
|
||||
if not item.get("reference_name"):
|
||||
frappe.throw(_("Please select a valid course or batch"))
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record dependencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
@@ -12,7 +11,6 @@ EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||
|
||||
|
||||
|
||||
class IntegrationTestLMSCoupon(IntegrationTestCase):
|
||||
"""
|
||||
Integration tests for LMSCoupon.
|
||||
|
||||
@@ -5,7 +5,7 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.email.doctype.email_template.email_template import get_email_template
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import add_days, nowdate, flt
|
||||
from frappe.utils import add_days, flt, nowdate
|
||||
|
||||
|
||||
class LMSPayment(Document):
|
||||
@@ -15,6 +15,7 @@ class LMSPayment(Document):
|
||||
gst = flt(self.gst_amount or 0, self.precision("gst_amount"))
|
||||
self.total_amount = flt(amount - discount + gst, self.precision("total_amount"))
|
||||
|
||||
|
||||
def send_payment_reminder():
|
||||
outgoing_email_account = frappe.get_cached_value(
|
||||
"Email Account", {"default_outgoing": 1, "enable_outgoing": 1}, "name"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def get_payment_gateway():
|
||||
return frappe.db.get_single_value("LMS Settings", "payment_gateway")
|
||||
|
||||
@@ -18,17 +19,17 @@ def validate_currency(payment_gateway, currency):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_payment_link(
|
||||
doctype,
|
||||
docname,
|
||||
title,
|
||||
amount,
|
||||
doctype,
|
||||
docname,
|
||||
title,
|
||||
amount,
|
||||
discount_amount,
|
||||
gst_amount,
|
||||
currency,
|
||||
address,
|
||||
redirect_to,
|
||||
payment_for_certificate,
|
||||
coupon_code=None,
|
||||
gst_amount,
|
||||
currency,
|
||||
address,
|
||||
redirect_to,
|
||||
payment_for_certificate,
|
||||
coupon_code=None,
|
||||
):
|
||||
payment_gateway = get_payment_gateway()
|
||||
address = frappe._dict(address)
|
||||
@@ -37,21 +38,22 @@ def get_payment_link(
|
||||
if doctype in ["LMS Course", "LMS Batch"] and coupon_code:
|
||||
try:
|
||||
from lms.lms.utils import apply_coupon
|
||||
|
||||
coupon_context = apply_coupon(doctype, docname, coupon_code)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
payment = record_payment(
|
||||
address,
|
||||
doctype,
|
||||
docname,
|
||||
amount,
|
||||
currency,
|
||||
discount_amount,
|
||||
gst_amount,
|
||||
payment_for_certificate,
|
||||
coupon_context,
|
||||
)
|
||||
address,
|
||||
doctype,
|
||||
docname,
|
||||
amount,
|
||||
currency,
|
||||
discount_amount,
|
||||
gst_amount,
|
||||
payment_for_certificate,
|
||||
coupon_context,
|
||||
)
|
||||
controller = get_controller(payment_gateway)
|
||||
|
||||
payment_details = {
|
||||
@@ -79,15 +81,15 @@ def get_payment_link(
|
||||
|
||||
|
||||
def record_payment(
|
||||
address,
|
||||
doctype,
|
||||
docname,
|
||||
amount,
|
||||
currency,
|
||||
discount_amount=0,
|
||||
gst_amount=0,
|
||||
payment_for_certificate=0,
|
||||
coupon_context=None,
|
||||
address,
|
||||
doctype,
|
||||
docname,
|
||||
amount,
|
||||
currency,
|
||||
discount_amount=0,
|
||||
gst_amount=0,
|
||||
payment_for_certificate=0,
|
||||
coupon_context=None,
|
||||
):
|
||||
address = frappe._dict(address)
|
||||
address_name = save_address(address)
|
||||
|
||||
112
lms/lms/utils.py
112
lms/lms/utils.py
@@ -953,74 +953,74 @@ def get_current_exchange_rate(source, target="USD"):
|
||||
|
||||
@frappe.whitelist()
|
||||
def apply_coupon(doctype, docname, code, country=None):
|
||||
# Validate doctype
|
||||
if doctype not in ["LMS Course", "LMS Batch"]:
|
||||
frappe.throw(_("Invalid doctype for coupon application."))
|
||||
# Validate doctype
|
||||
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 not code:
|
||||
frappe.throw(_("Coupon code is required."))
|
||||
|
||||
summary = get_order_summary(doctype, docname, country)
|
||||
summary = get_order_summary(doctype, docname, country)
|
||||
|
||||
base_amount = summary.original_amount
|
||||
currency = summary.currency
|
||||
base_amount = summary.original_amount
|
||||
currency = summary.currency
|
||||
|
||||
# Fetch coupon case-insensitively
|
||||
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."))
|
||||
# Fetch coupon case-insensitively
|
||||
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)
|
||||
coupon = frappe.get_doc("LMS Coupon", coupon_name)
|
||||
|
||||
# Expiry
|
||||
if coupon.expires_on and getdate(coupon.expires_on) < getdate():
|
||||
frappe.throw(_("This coupon has expired."))
|
||||
# Expiry
|
||||
if coupon.expires_on and getdate(coupon.expires_on) < getdate():
|
||||
frappe.throw(_("This coupon has expired."))
|
||||
|
||||
# Usage limit
|
||||
if coupon.usage_limit and cint(coupon.times_redeemed) >= cint(coupon.usage_limit):
|
||||
frappe.throw(_("This coupon has reached its usage limit."))
|
||||
# Usage limit
|
||||
if coupon.usage_limit and cint(coupon.times_redeemed) >= cint(coupon.usage_limit):
|
||||
frappe.throw(_("This coupon has reached its usage limit."))
|
||||
|
||||
# Applicability (if rows exist, must match; if none, applies to all)
|
||||
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."))
|
||||
# Applicability (if rows exist, must match; if none, applies to all)
|
||||
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."))
|
||||
|
||||
# Compute discount before tax
|
||||
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))
|
||||
# Compute discount before tax
|
||||
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)
|
||||
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)
|
||||
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,
|
||||
}
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user