Merge branch 'develop' of https://github.com/frappe/lms into consent-on-billing-page

This commit is contained in:
Jannat Patel
2025-12-23 13:09:53 +05:30
20 changed files with 397 additions and 546 deletions

View File

@@ -147,6 +147,29 @@ def verify_billing_access(doctype, name, billing_type):
return access, message
@frappe.whitelist(allow_guest=True)
def get_job_details(job):
return frappe.db.get_value(
"Job Opportunity",
job,
[
"job_title",
"location",
"country",
"type",
"work_mode",
"company_name",
"company_logo",
"company_website",
"name",
"creation",
"description",
"owner",
],
as_dict=1,
)
@frappe.whitelist(allow_guest=True)
def get_job_opportunities(filters=None, orFilters=None):
if not filters:

View File

@@ -43,8 +43,12 @@ class TestCourseEvaluator(UnitTestCase):
def calculated_first_date_of_schedule(self):
today = getdate()
offset = (0 - today.weekday() + 7) % 7 # 0 for Monday
first_date = add_days(today, offset)
offset_monday = (0 - today.weekday() + 7) % 7 # 0 for Monday
offset_wednesday = (2 - today.weekday() + 7) % 7 # 2 for Wednesday
if offset_monday < offset_wednesday:
first_date = add_days(today, offset_monday)
else:
first_date = add_days(today, offset_wednesday)
return first_date
def calculated_last_date_of_schedule(self, first_date):

View File

@@ -9,10 +9,11 @@
"engine": "InnoDB",
"field_order": [
"title",
"question",
"column_break_hmwv",
"type",
"grade_assignment",
"course",
"column_break_hmwv",
"question",
"section_break_sjti",
"show_answer",
"answer"
@@ -68,12 +69,18 @@
{
"fieldname": "section_break_sjti",
"fieldtype": "Section Break"
},
{
"fieldname": "course",
"fieldtype": "Link",
"label": "Course",
"options": "LMS Course"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2025-12-17 14:47:05.489937",
"modified": "2025-12-19 16:30:58.531722",
"modified_by": "sayali@frappe.io",
"module": "LMS",
"name": "LMS Assignment",

View File

@@ -377,9 +377,13 @@
{
"link_doctype": "LMS Certificate",
"link_fieldname": "batch_name"
},
{
"link_doctype": "LMS Payment",
"link_fieldname": "payment_for_document"
}
],
"modified": "2025-12-04 12:54:11.190967",
"modified": "2025-12-23 11:27:00.424331",
"modified_by": "sayali@frappe.io",
"module": "LMS",
"name": "LMS Batch",

View File

@@ -30,7 +30,6 @@ class LMSBatch(Document):
self.validate_payments_app()
self.validate_amount_and_currency()
self.validate_duplicate_assessments()
self.validate_membership()
self.validate_timetable()
self.validate_evaluation_end_date()
@@ -82,16 +81,6 @@ class LMSBatch(Document):
if self.evaluation_end_date and self.evaluation_end_date < self.end_date:
frappe.throw(_("Evaluation end date cannot be less than the batch end date."))
def validate_membership(self):
members = frappe.get_all("LMS Batch Enrollment", {"batch": self.name}, pluck="member")
for course in self.courses:
for member in members:
if not frappe.db.exists("LMS Enrollment", {"course": course.course, "member": member}):
enrollment = frappe.new_doc("LMS Enrollment")
enrollment.course = course.course
enrollment.member = member
enrollment.save()
def validate_seats_left(self):
if cint(self.seat_count) < 0:
frappe.throw(_("Seat count cannot be negative."))

View File

@@ -17,6 +17,8 @@ class LMSBatchEnrollment(Document):
def validate(self):
self.validate_owner()
self.validate_duplicate_members()
self.validate_payment()
self.validate_self_enrollment()
self.validate_seat_availability()
self.validate_course_enrollment()
@@ -28,6 +30,30 @@ class LMSBatchEnrollment(Document):
if "Moderator" not in roles and "Batch Evaluator" not in roles:
frappe.throw(_("You must be a Moderator or Batch Evaluator to enroll users in a batch."))
def validate_payment(self):
paid_batch = frappe.db.get_value("LMS Batch", self.batch, "paid_batch")
if paid_batch:
payment = frappe.db.exists(
"LMS Payment",
{
"payment_for_document_type": "LMS Batch",
"payment_for_document": self.batch,
"member": self.member,
"payment_received": True,
},
)
if not payment:
frappe.throw(_("Payment is required to enroll in this batch."))
def validate_self_enrollment(self):
allow_self_enrollment = frappe.db.get_value("LMS Batch", self.batch, "allow_self_enrollment")
if not allow_self_enrollment and not self.is_admin():
frappe.throw(_("Enrollment in this batch is restricted. Please contact the Administrator."))
def is_admin(self):
roles = frappe.get_roles(frappe.session.user)
return "Course Creator" in roles or "Moderator" in roles or "Batch Evaluator" in roles
def validate_duplicate_members(self):
if frappe.db.exists(
"LMS Batch Enrollment",
@@ -52,6 +78,7 @@ class LMSBatchEnrollment(Document):
enrollment = frappe.new_doc("LMS Enrollment")
enrollment.course = course.course
enrollment.member = self.member
enrollment.enrollment_from_batch = self.batch
enrollment.save()
def add_member_to_live_class(self):

View File

@@ -67,6 +67,7 @@ class LMSCertificateRequest(Document):
{
"evaluator": self.evaluator,
"date": self.date,
"status": ["!=", "Cancelled"],
"start_time": self.start_time,
"member": ["!=", self.member],
},

View File

@@ -10,6 +10,7 @@
"progress",
"payment",
"current_lesson",
"enrollment_from_batch",
"column_break_3",
"member",
"member_name",
@@ -129,13 +130,19 @@
"fieldname": "member_image",
"fieldtype": "Attach Image",
"label": "Member Image"
},
{
"fieldname": "enrollment_from_batch",
"fieldtype": "Link",
"label": "Enrollment from Batch",
"options": "LMS Batch"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2025-12-15 21:27:30.733483",
"modified_by": "Administrator",
"modified": "2025-12-23 11:11:23.908797",
"modified_by": "sayali@frappe.io",
"module": "LMS",
"name": "LMS Enrollment",
"owner": "Administrator",

View File

@@ -29,6 +29,9 @@ class LMSEnrollment(Document):
)
)
if self.enrollment_from_batch:
return
if not course_details.published:
frappe.throw(_("You cannot enroll in an unpublished course."))

View File

@@ -76,6 +76,7 @@
],
"fields": [
{
"default": "https://falcon.frappe.io/",
"fieldname": "livecode_url",
"fieldtype": "Data",
"label": "LiveCode URL"
@@ -451,7 +452,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2025-12-10 17:36:15.689695",
"modified": "2025-12-22 11:30:13.868031",
"modified_by": "sayali@frappe.io",
"module": "LMS",
"name": "LMS Settings",

View File

@@ -1775,11 +1775,7 @@ def enroll_in_batch(batch, payment_name=None):
if not frappe.db.exists("LMS Batch", batch):
frappe.throw(_("The specified batch does not exist."))
batch_doc = frappe.db.get_value(
"LMS Batch", batch, ["name", "seat_count", "allow_self_enrollment", "paid_batch"], as_dict=True
)
payment_doc = get_payment_details(payment_name)
validate_enrollment_eligibility(batch_doc, payment_doc)
create_enrollment(batch, payment_doc)
@@ -1792,22 +1788,6 @@ def get_payment_details(payment_name):
return payment_doc
def validate_enrollment_eligibility(batch_doc, payment_doc=None):
if frappe.db.exists("LMS Batch Enrollment", {"batch": batch_doc.name, "member": frappe.session.user}):
frappe.throw(_("You are already enrolled in this batch."))
if batch_doc.paid_batch:
if not payment_doc or not payment_doc.payment_received:
frappe.throw(_("Payment is required to enroll in this batch."))
elif not batch_doc.allow_self_enrollment:
frappe.throw(_("Enrollment in this batch is restricted. Please contact the Administrator."))
students = frappe.db.count("LMS Batch Enrollment", {"batch": batch_doc.name})
if batch_doc.seat_count and students >= batch_doc.seat_count:
frappe.throw(_("There are no seats available in this batch."))
def create_enrollment(batch, payment_doc=None):
new_student = frappe.new_doc("LMS Batch Enrollment")
new_student.update(

File diff suppressed because it is too large Load Diff