Merge pull request #619 from pateljannat/state-validation
fix: billing flow
This commit is contained in:
@@ -12,7 +12,12 @@ frappe.ui.form.on("LMS Batch", {
|
||||
});
|
||||
|
||||
frm.set_query("reference_doctype", "timetable", function () {
|
||||
let doctypes = ["Course Lesson", "LMS Quiz", "LMS Assignment"];
|
||||
let doctypes = [
|
||||
"Course Lesson",
|
||||
"LMS Quiz",
|
||||
"LMS Assignment",
|
||||
"LMS Live Class",
|
||||
];
|
||||
return {
|
||||
filters: {
|
||||
name: ["in", doctypes],
|
||||
|
||||
@@ -146,8 +146,9 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "category",
|
||||
"fieldtype": "Autocomplete",
|
||||
"label": "Category"
|
||||
"fieldtype": "Link",
|
||||
"label": "Category",
|
||||
"options": "LMS Category"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_ubxi",
|
||||
@@ -221,7 +222,7 @@
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2023-09-20 11:25:10.683688",
|
||||
"modified": "2023-09-20 14:40:45.940540",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "LMS Batch",
|
||||
|
||||
@@ -18,6 +18,7 @@ from frappe.utils import (
|
||||
get_datetime,
|
||||
getdate,
|
||||
validate_phone_number,
|
||||
ceil,
|
||||
)
|
||||
from frappe.utils.dateutils import get_period
|
||||
from lms.lms.md import find_macros, markdown_to_html
|
||||
@@ -844,7 +845,7 @@ def get_payment_options(doctype, docname, phone, country):
|
||||
validate_phone_number(phone, True)
|
||||
details = get_details(doctype, docname)
|
||||
details.amount, details.currency = check_multicurrency(
|
||||
details.amount, details.currency
|
||||
details.amount, details.currency, country
|
||||
)
|
||||
if details.currency == "INR":
|
||||
details.amount, details.gst_applied = apply_gst(details.amount, country)
|
||||
@@ -868,18 +869,20 @@ def get_payment_options(doctype, docname, phone, country):
|
||||
return options
|
||||
|
||||
|
||||
def check_multicurrency(amount, currency):
|
||||
def check_multicurrency(amount, currency, country=None):
|
||||
show_usd_equivalent = frappe.db.get_single_value("LMS Settings", "show_usd_equivalent")
|
||||
exception_country = frappe.get_all(
|
||||
"Payment Country", filters={"parent": "LMS Settings"}, pluck="country"
|
||||
)
|
||||
apply_rounding = frappe.db.get_single_value("LMS Settings", "apply_rounding")
|
||||
country = frappe.db.get_value("User", frappe.session.user, "country")
|
||||
country = country or frappe.db.get_value(
|
||||
"Address", {"email_id": frappe.session.user}, "country"
|
||||
)
|
||||
|
||||
if not show_usd_equivalent or currency == "USD":
|
||||
return amount, currency
|
||||
|
||||
if exception_country and country in exception_country:
|
||||
if not country or (exception_country and country in exception_country):
|
||||
return amount, currency
|
||||
|
||||
exchange_rate = get_current_exchange_rate(currency, "USD")
|
||||
@@ -887,7 +890,7 @@ def check_multicurrency(amount, currency):
|
||||
currency = "USD"
|
||||
|
||||
if apply_rounding and amount % 100 != 0:
|
||||
amount = amount + 100 - amount % 100
|
||||
amount = ceil(amount + 100 - amount % 100)
|
||||
|
||||
return amount, currency
|
||||
|
||||
@@ -930,7 +933,15 @@ def get_details(doctype, docname):
|
||||
|
||||
|
||||
def save_address(address):
|
||||
address.update(
|
||||
filters = {"email_id": frappe.session.user}
|
||||
exists = frappe.db.exists("Address", filters)
|
||||
if exists:
|
||||
address_doc = frappe.get_last_doc("Address", filters=filters)
|
||||
else:
|
||||
address_doc = frappe.new_doc("Address")
|
||||
|
||||
address_doc.update(address)
|
||||
address_doc.update(
|
||||
{
|
||||
"address_title": frappe.db.get_value("User", frappe.session.user, "full_name"),
|
||||
"address_type": "Billing",
|
||||
@@ -938,10 +949,8 @@ def save_address(address):
|
||||
"email_id": frappe.session.user,
|
||||
}
|
||||
)
|
||||
doc = frappe.new_doc("Address")
|
||||
doc.update(address)
|
||||
doc.save(ignore_permissions=True)
|
||||
return doc.name
|
||||
address_doc.save(ignore_permissions=True)
|
||||
return address_doc.name
|
||||
|
||||
|
||||
def get_client():
|
||||
@@ -1064,3 +1073,10 @@ def get_current_exchange_rate(source, target="USD"):
|
||||
response = requests.request("GET", url)
|
||||
details = response.json()
|
||||
return details["rates"][target]
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def change_currency(amount, currency, country=None):
|
||||
amount = cint(amount)
|
||||
amount, currency = check_multicurrency(amount, currency, country)
|
||||
return fmt_money(amount, 0, currency)
|
||||
|
||||
@@ -2442,4 +2442,15 @@ select {
|
||||
justify-content: space-between;
|
||||
width: 50%;
|
||||
margin: 0 auto 1rem;
|
||||
}
|
||||
|
||||
.batch-details {
|
||||
width: 50%;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
.batch-details {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -320,6 +320,7 @@ const open_batch_dialog = () => {
|
||||
label: __("Category"),
|
||||
fieldname: "category",
|
||||
options: "LMS Category",
|
||||
only_select: 1,
|
||||
default: batch_info && batch_info.category,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from frappe import _
|
||||
import frappe
|
||||
from frappe.utils import getdate, get_datetime
|
||||
from frappe.utils import getdate
|
||||
from lms.www.utils import get_assessments, is_student
|
||||
from lms.lms.utils import (
|
||||
has_course_moderator_role,
|
||||
@@ -52,14 +52,14 @@ def get_context(context):
|
||||
"Batch Course",
|
||||
{"parent": batch_name},
|
||||
["name", "course", "title"],
|
||||
order_by="creation desc",
|
||||
order_by="idx",
|
||||
)
|
||||
|
||||
batch_students = frappe.get_all(
|
||||
"Batch Student",
|
||||
{"parent": batch_name},
|
||||
["name", "student", "student_name", "username"],
|
||||
order_by="creation desc",
|
||||
order_by="idx",
|
||||
)
|
||||
|
||||
context.batch_courses = get_class_course_details(batch_courses)
|
||||
|
||||
@@ -164,24 +164,26 @@
|
||||
|
||||
|
||||
{% macro BatchDetails(batch_info) %}
|
||||
<div class="course-description-section w-50">
|
||||
<div class="mt-2">
|
||||
{{ batch_info.batch_details }}
|
||||
</div>
|
||||
<div class="batch-details">
|
||||
{{ batch_info.batch_details }}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro CourseList(courses) %}
|
||||
<div class="batch-course-list">
|
||||
{% if is_moderator %}
|
||||
<button class="btn btn-default btn-sm btn-add-course pull-right">
|
||||
{{ _("Add Courses") }}
|
||||
</button>
|
||||
{% endif %}
|
||||
<div class="page-title">
|
||||
{{ _("Courses") }}
|
||||
|
||||
<div class="flex align-center">
|
||||
<div class="page-title">
|
||||
{{ _("Courses") }}
|
||||
</div>
|
||||
{% if is_moderator %}
|
||||
<button class="btn btn-default btn-sm btn-add-course ml-4">
|
||||
{{ _("Add Course") }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if courses | length %}
|
||||
<div class="cards-parent mt-2">
|
||||
{% for course in courses %}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import frappe
|
||||
from frappe import _
|
||||
from lms.lms.utils import has_course_moderator_role, has_course_evaluator_role
|
||||
from lms.lms.utils import (
|
||||
has_course_moderator_role,
|
||||
has_course_evaluator_role,
|
||||
check_multicurrency,
|
||||
)
|
||||
from lms.www.utils import is_student
|
||||
|
||||
|
||||
@@ -29,6 +33,13 @@ def get_context(context):
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
if context.batch_info.amount and context.batch_info.currency:
|
||||
amount, currency = check_multicurrency(
|
||||
context.batch_info.amount, context.batch_info.currency
|
||||
)
|
||||
context.batch_info.amount = amount
|
||||
context.batch_info.currency = currency
|
||||
|
||||
context.is_moderator = has_course_moderator_role()
|
||||
context.is_evaluator = has_course_evaluator_role()
|
||||
context.is_student = is_student(batch_name)
|
||||
@@ -44,7 +55,7 @@ def get_context(context):
|
||||
"Batch Course",
|
||||
{"parent": batch_name},
|
||||
["name as batch_course", "course", "title", "evaluator"],
|
||||
order_by="creation desc",
|
||||
order_by="idx",
|
||||
)
|
||||
|
||||
for course in context.courses:
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import frappe
|
||||
from frappe.utils import getdate
|
||||
from lms.lms.utils import has_course_moderator_role, has_course_evaluator_role
|
||||
from lms.lms.utils import (
|
||||
has_course_moderator_role,
|
||||
has_course_evaluator_role,
|
||||
check_multicurrency,
|
||||
)
|
||||
|
||||
|
||||
def get_context(context):
|
||||
@@ -28,6 +32,12 @@ def get_context(context):
|
||||
for batch in batches:
|
||||
batch.student_count = frappe.db.count("Batch Student", {"parent": batch.name})
|
||||
batch.course_count = frappe.db.count("Batch Course", {"parent": batch.name})
|
||||
|
||||
if batch.amount and batch.currency:
|
||||
amount, currency = check_multicurrency(batch.amount, batch.currency)
|
||||
batch.amount = amount
|
||||
batch.currency = currency
|
||||
|
||||
batch.seats_left = (
|
||||
batch.seat_count - batch.student_count if batch.seat_count else None
|
||||
)
|
||||
|
||||
@@ -37,7 +37,8 @@
|
||||
|
||||
<div class="flex">
|
||||
<div class="field-label">
|
||||
{{ _("Total Price: ") }} {{ frappe.utils.fmt_money(amount, 2, currency) }}
|
||||
{{ _("Total Price: ") }}
|
||||
<span class="total-price">{{ frappe.utils.fmt_money(amount, 2, currency) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% if gst_applied %}
|
||||
@@ -64,4 +65,11 @@
|
||||
{%- block script %}
|
||||
{{ super() }}
|
||||
<script src="https://checkout.razorpay.com/v1/checkout.js"></script>
|
||||
<script>
|
||||
const address = {{ address if address else 0 }};
|
||||
const amount = {{ amount }};
|
||||
const currency = "{{ currency }}";
|
||||
const exception_country = {{ exception_country }};
|
||||
const original_price_formatted = "{{ frappe.utils.fmt_money(original_amount, 0, original_currency) }}"
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -18,23 +18,27 @@ const setup_billing = () => {
|
||||
label: __("Billing Name"),
|
||||
fieldname: "billing_name",
|
||||
reqd: 1,
|
||||
default: address && address.billing_name,
|
||||
},
|
||||
{
|
||||
fieldtype: "Data",
|
||||
label: __("Address Line 1"),
|
||||
fieldname: "address_line1",
|
||||
reqd: 1,
|
||||
default: address && address.address_line1,
|
||||
},
|
||||
{
|
||||
fieldtype: "Data",
|
||||
label: __("Address Line 2"),
|
||||
fieldname: "address_line2",
|
||||
default: address && address.address_line2,
|
||||
},
|
||||
{
|
||||
fieldtype: "Data",
|
||||
label: __("City/Town"),
|
||||
fieldname: "city",
|
||||
reqd: 1,
|
||||
default: address && address.city,
|
||||
},
|
||||
{
|
||||
fieldtype: "Column Break",
|
||||
@@ -43,6 +47,7 @@ const setup_billing = () => {
|
||||
fieldtype: "Data",
|
||||
label: __("State/Province"),
|
||||
fieldname: "state",
|
||||
default: address && address.state,
|
||||
},
|
||||
{
|
||||
fieldtype: "Link",
|
||||
@@ -51,18 +56,24 @@ const setup_billing = () => {
|
||||
options: "Country",
|
||||
reqd: 1,
|
||||
only_select: 1,
|
||||
default: address && address.country,
|
||||
change: () => {
|
||||
change_currency();
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldtype: "Data",
|
||||
label: __("Postal Code"),
|
||||
fieldname: "pincode",
|
||||
reqd: 1,
|
||||
default: address && address.pincode,
|
||||
},
|
||||
{
|
||||
fieldtype: "Data",
|
||||
label: __("Phone Number"),
|
||||
fieldname: "phone",
|
||||
reqd: 1,
|
||||
default: address && address.phone,
|
||||
},
|
||||
{
|
||||
fieldtype: "Section Break",
|
||||
@@ -143,3 +154,33 @@ const handle_success = (response, doctype, docname, address, order_id) => {
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const change_currency = () => {
|
||||
let country = this.billing.get_value("country");
|
||||
if (exception_country.includes(country)) {
|
||||
update_price(original_price_formatted);
|
||||
return;
|
||||
}
|
||||
frappe.call({
|
||||
method: "lms.lms.utils.change_currency",
|
||||
args: {
|
||||
country: country,
|
||||
amount: amount,
|
||||
currency: currency,
|
||||
},
|
||||
callback: (data) => {
|
||||
let current_price = $(".total-price").text();
|
||||
if (current_price != data.message) {
|
||||
update_price(data.message);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const update_price = (price) => {
|
||||
$(".total-price").text(price);
|
||||
frappe.show_alert({
|
||||
message: "Total Price has been updated.",
|
||||
indicator: "yellow",
|
||||
});
|
||||
};
|
||||
|
||||
@@ -14,10 +14,17 @@ def get_context(context):
|
||||
|
||||
validate_access(doctype, docname, module)
|
||||
get_billing_details(context)
|
||||
context.original_amount = context.amount
|
||||
context.original_currency = context.currency
|
||||
context.exception_country = frappe.get_all(
|
||||
"Payment Country", filters={"parent": "LMS Settings"}, pluck="country"
|
||||
)
|
||||
|
||||
context.amount, context.currency = check_multicurrency(
|
||||
context.amount, context.currency
|
||||
)
|
||||
|
||||
context.address = get_address()
|
||||
if context.currency == "INR":
|
||||
context.amount, context.gst_applied = apply_gst(context.amount, None)
|
||||
|
||||
@@ -75,3 +82,35 @@ def get_billing_details(context):
|
||||
context.title = details.title
|
||||
context.amount = details.amount
|
||||
context.currency = details.currency
|
||||
|
||||
|
||||
def get_address():
|
||||
address = frappe.get_all(
|
||||
"Address",
|
||||
{"email_id": frappe.session.user},
|
||||
[
|
||||
"address_title as billing_name",
|
||||
"address_line1",
|
||||
"address_line2",
|
||||
"city",
|
||||
"state",
|
||||
"country",
|
||||
"pincode",
|
||||
"phone",
|
||||
],
|
||||
order_by="creation desc",
|
||||
limit=1,
|
||||
)
|
||||
|
||||
if not len(address):
|
||||
return None
|
||||
else:
|
||||
address = address[0]
|
||||
|
||||
if not address.address_line2:
|
||||
address.address_line2 = ""
|
||||
|
||||
if not address.state:
|
||||
address.state = ""
|
||||
|
||||
return address
|
||||
|
||||
@@ -10,6 +10,7 @@ from lms.lms.utils import (
|
||||
is_instructor,
|
||||
redirect_to_courses_list,
|
||||
get_average_rating,
|
||||
check_multicurrency,
|
||||
)
|
||||
|
||||
|
||||
@@ -60,6 +61,11 @@ def set_course_context(context, course_name):
|
||||
as_dict=True,
|
||||
)
|
||||
|
||||
if course.course_price:
|
||||
course.course_price, course.currency = check_multicurrency(
|
||||
course.course_price, course.currency
|
||||
)
|
||||
|
||||
if frappe.form_dict.get("edit"):
|
||||
if not is_instructor(course.name) and not has_course_moderator_role():
|
||||
raise frappe.PermissionError(_("You do not have permission to access this page."))
|
||||
|
||||
@@ -7,6 +7,7 @@ from lms.lms.utils import (
|
||||
has_course_moderator_role,
|
||||
get_courses_under_review,
|
||||
get_average_rating,
|
||||
check_multicurrency,
|
||||
)
|
||||
from lms.overrides.user import get_enrolled_courses, get_authored_courses
|
||||
|
||||
@@ -58,6 +59,12 @@ def get_courses():
|
||||
course.enrollment_count = frappe.db.count(
|
||||
"LMS Enrollment", {"course": course.name, "member_type": "Student"}
|
||||
)
|
||||
|
||||
if course.course_price:
|
||||
course.course_price, course.currency = check_multicurrency(
|
||||
course.course_price, course.currency
|
||||
)
|
||||
|
||||
course.avg_rating = get_average_rating(course.name) or 0
|
||||
if course.upcoming:
|
||||
upcoming_courses.append(course)
|
||||
|
||||
Reference in New Issue
Block a user