fix: misc batch enrollment issues
This commit is contained in:
@@ -333,7 +333,7 @@ const getChartData = () => {
|
||||
})
|
||||
|
||||
Object.keys(student.assessments).forEach((assessment) => {
|
||||
if (student.assessments[assessment] === 100) {
|
||||
if (student.assessments[assessment].result === 'Passed') {
|
||||
categories[assessment].value += 1
|
||||
}
|
||||
})
|
||||
|
||||
@@ -316,6 +316,9 @@ const quiz = createResource({
|
||||
},
|
||||
cache: ['quiz', props.quizName],
|
||||
auto: true,
|
||||
transform(data) {
|
||||
data.duration = parseInt(data.duration)
|
||||
},
|
||||
onSuccess(data) {
|
||||
populateQuestions()
|
||||
setupTimer()
|
||||
|
||||
@@ -44,7 +44,7 @@ const certificates = createListResource({
|
||||
filters: {
|
||||
member: props.profile.data?.name,
|
||||
},
|
||||
fields: ['name', 'course_title', 'batch_title', 'issue_date'],
|
||||
fields: ['name', 'course_title', 'batch_title', 'issue_date', 'template'],
|
||||
cache: ['certificates', props.profile.data?.name],
|
||||
})
|
||||
|
||||
|
||||
@@ -113,7 +113,10 @@ scheduler_events = {
|
||||
"lms.lms.doctype.lms_certificate_request.lms_certificate_request.schedule_evals",
|
||||
"lms.lms.api.update_course_statistics",
|
||||
],
|
||||
"daily": ["lms.job.doctype.job_opportunity.job_opportunity.update_job_openings"],
|
||||
"daily": [
|
||||
"lms.job.doctype.job_opportunity.job_opportunity.update_job_openings",
|
||||
"lms.lms.doctype.lms_payment.lms_payment.send_payment_reminder",
|
||||
],
|
||||
}
|
||||
|
||||
fixtures = ["Custom Field", "Function", "Industry", "LMS Category"]
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
// Copyright (c) 2022, Frappe and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Batch Student", {
|
||||
// refresh: function(frm) {
|
||||
// }
|
||||
});
|
||||
@@ -1,83 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"creation": "2022-11-09 16:20:44.602545",
|
||||
"default_view": "List",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"student_details_section",
|
||||
"student",
|
||||
"student_name",
|
||||
"username",
|
||||
"column_break_oduu",
|
||||
"payment",
|
||||
"source",
|
||||
"confirmation_email_sent"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "student",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Student",
|
||||
"options": "User",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "student.full_name",
|
||||
"fieldname": "student_name",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Student Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "student.username",
|
||||
"fieldname": "username",
|
||||
"fieldtype": "Data",
|
||||
"label": "Username",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "student_details_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Student Details"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_oduu",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "payment",
|
||||
"fieldtype": "Link",
|
||||
"label": "Payment",
|
||||
"options": "LMS Payment"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "confirmation_email_sent",
|
||||
"fieldtype": "Check",
|
||||
"label": "Confirmation Email Sent"
|
||||
},
|
||||
{
|
||||
"fieldname": "source",
|
||||
"fieldtype": "Link",
|
||||
"label": "Source",
|
||||
"options": "LMS Source"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-10-26 16:52:04.266694",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "Batch Student",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
# Copyright (c) 2022, Frappe and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class BatchStudent(Document):
|
||||
pass
|
||||
@@ -1,9 +0,0 @@
|
||||
# Copyright (c) 2022, Frappe and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.tests import UnitTestCase
|
||||
|
||||
|
||||
class TestBatchStudent(UnitTestCase):
|
||||
pass
|
||||
@@ -1,8 +1,20 @@
|
||||
// Copyright (c) 2025, Frappe and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
// frappe.ui.form.on("LMS Batch Enrollment", {
|
||||
// refresh(frm) {
|
||||
|
||||
// },
|
||||
// });
|
||||
frappe.ui.form.on("LMS Batch Enrollment", {
|
||||
refresh(frm) {
|
||||
if (!frm.doc.confirmation_email_sent) {
|
||||
frm.add_custom_button(__("Send Confirmation Email"), function () {
|
||||
frappe.call({
|
||||
method: "lms.lms.doctype.lms_batch_enrollment.lms_batch_enrollment.send_confirmation_email",
|
||||
args: {
|
||||
doc: frm.doc,
|
||||
},
|
||||
callback: function (r) {
|
||||
frm.refresh();
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
import json
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.email.doctype.email_template.email_template import get_email_template
|
||||
@@ -9,7 +10,7 @@ from frappe.email.doctype.email_template.email_template import get_email_templat
|
||||
|
||||
class LMSBatchEnrollment(Document):
|
||||
def after_insert(self):
|
||||
self.send_confirmation_email()
|
||||
send_confirmation_email(self)
|
||||
self.add_member_to_live_class()
|
||||
|
||||
def validate(self):
|
||||
@@ -37,53 +38,6 @@ class LMSBatchEnrollment(Document):
|
||||
enrollment.member = self.member
|
||||
enrollment.save()
|
||||
|
||||
def send_confirmation_email(self):
|
||||
if not self.confirmation_email_sent:
|
||||
outgoing_email_account = frappe.get_cached_value(
|
||||
"Email Account", {"default_outgoing": 1, "enable_outgoing": 1}, "name"
|
||||
)
|
||||
if not self.confirmation_email_sent and (
|
||||
outgoing_email_account or frappe.conf.get("mail_login")
|
||||
):
|
||||
self.send_mail()
|
||||
self.db_set("confirmation_email_sent", 1)
|
||||
|
||||
def send_mail(self):
|
||||
subject = _("Enrollment Confirmation for the Next Training Batch")
|
||||
template = "batch_confirmation"
|
||||
custom_template = frappe.db.get_single_value(
|
||||
"LMS Settings", "batch_confirmation_template"
|
||||
)
|
||||
batch = frappe.db.get_value(
|
||||
"LMS Batch",
|
||||
self.batch,
|
||||
["name", "title", "start_date", "start_time", "medium"],
|
||||
as_dict=1,
|
||||
)
|
||||
args = {
|
||||
"title": batch.title,
|
||||
"student_name": self.member_name,
|
||||
"start_time": batch.start_time,
|
||||
"start_date": batch.start_date,
|
||||
"medium": batch.medium,
|
||||
"name": batch.name,
|
||||
}
|
||||
|
||||
if custom_template:
|
||||
email_template = get_email_template(custom_template, args)
|
||||
subject = email_template.get("subject")
|
||||
content = email_template.get("message")
|
||||
|
||||
frappe.sendmail(
|
||||
recipients=self.member,
|
||||
subject=subject,
|
||||
template=template if not custom_template else None,
|
||||
content=content if custom_template else None,
|
||||
args=args,
|
||||
header=[subject, "green"],
|
||||
retry=3,
|
||||
)
|
||||
|
||||
def add_member_to_live_class(self):
|
||||
live_classes = frappe.get_all(
|
||||
"LMS Live Class", {"batch_name": self.batch}, ["name", "event"]
|
||||
@@ -102,3 +56,56 @@ class LMSBatchEnrollment(Document):
|
||||
"parentfield": "event_participants",
|
||||
}
|
||||
).save()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def send_confirmation_email(doc):
|
||||
if isinstance(doc, str):
|
||||
doc = frappe._dict(json.loads(doc))
|
||||
|
||||
if not doc.confirmation_email_sent:
|
||||
outgoing_email_account = frappe.get_cached_value(
|
||||
"Email Account", {"default_outgoing": 1, "enable_outgoing": 1}, "name"
|
||||
)
|
||||
if not doc.confirmation_email_sent and (
|
||||
outgoing_email_account or frappe.conf.get("mail_login")
|
||||
):
|
||||
doc.send_mail()
|
||||
doc.db_set("confirmation_email_sent", 1)
|
||||
|
||||
|
||||
def send_mail(doc):
|
||||
subject = _("Enrollment Confirmation for the Next Training Batch")
|
||||
template = "batch_confirmation"
|
||||
custom_template = frappe.db.get_single_value(
|
||||
"LMS Settings", "batch_confirmation_template"
|
||||
)
|
||||
batch = frappe.db.get_value(
|
||||
"LMS Batch",
|
||||
doc.batch,
|
||||
["name", "title", "start_date", "start_time", "medium"],
|
||||
as_dict=1,
|
||||
)
|
||||
args = {
|
||||
"title": batch.title,
|
||||
"student_name": doc.member_name,
|
||||
"start_time": batch.start_time,
|
||||
"start_date": batch.start_date,
|
||||
"medium": batch.medium,
|
||||
"name": batch.name,
|
||||
}
|
||||
|
||||
if custom_template:
|
||||
email_template = get_email_template(custom_template, args)
|
||||
subject = email_template.get("subject")
|
||||
content = email_template.get("message")
|
||||
|
||||
frappe.sendmail(
|
||||
recipients=doc.member,
|
||||
subject=subject,
|
||||
template=template if not custom_template else None,
|
||||
content=content if custom_template else None,
|
||||
args=args,
|
||||
header=[subject, "green"],
|
||||
retry=3,
|
||||
)
|
||||
|
||||
@@ -1,9 +1,78 @@
|
||||
# Copyright (c) 2023, Frappe and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import add_days, nowdate
|
||||
from frappe.email.doctype.email_template.email_template import get_email_template
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class LMSPayment(Document):
|
||||
pass
|
||||
|
||||
|
||||
def send_payment_reminder():
|
||||
outgoing_email_account = frappe.get_cached_value(
|
||||
"Email Account", {"default_outgoing": 1, "enable_outgoing": 1}, "name"
|
||||
)
|
||||
|
||||
if not (outgoing_email_account or frappe.conf.get("mail_login")):
|
||||
return
|
||||
|
||||
incomplete_payments = frappe.get_all(
|
||||
"LMS Payment",
|
||||
{"payment_received": 0, "creation": [">", add_days(nowdate(), -1)]},
|
||||
fields=[
|
||||
"name",
|
||||
"member",
|
||||
"payment_for_document",
|
||||
"payment_for_document_type",
|
||||
"billing_name",
|
||||
],
|
||||
)
|
||||
|
||||
for payment in incomplete_payments:
|
||||
send_mail(payment)
|
||||
|
||||
|
||||
def send_mail(payment):
|
||||
subject = _(" Complete Your Enrollment - Don't miss out!")
|
||||
template = "payment_reminder"
|
||||
custom_template = frappe.db.get_single_value(
|
||||
"LMS Settings", "payment_reminder_template"
|
||||
)
|
||||
|
||||
args = {
|
||||
"billing_name": payment.billing_name,
|
||||
"type": payment.payment_for_document_type.split(" ")[-1].lower(),
|
||||
"title": frappe.db.get_value(
|
||||
payment.payment_for_document_type, payment.payment_for_document, "title"
|
||||
),
|
||||
"link": f"/lms/billing/{ payment.payment_for_document_type.split(' ')[-1].lower() }/{ payment.payment_for_document }",
|
||||
}
|
||||
|
||||
if custom_template:
|
||||
email_template = get_email_template(custom_template, args)
|
||||
subject = email_template.get("subject")
|
||||
content = email_template.get("message")
|
||||
|
||||
instructors = frappe.get_all(
|
||||
"Course Instructor",
|
||||
{
|
||||
"parenttype": payment.payment_for_document_type,
|
||||
"parent": payment.payment_for_document,
|
||||
},
|
||||
pluck="instructor",
|
||||
)
|
||||
|
||||
frappe.sendmail(
|
||||
recipients=payment.member,
|
||||
cc=instructors,
|
||||
subject=subject,
|
||||
template=template if not custom_template else None,
|
||||
content=content if custom_template else None,
|
||||
args=args,
|
||||
header=[subject, "green"],
|
||||
retry=3,
|
||||
)
|
||||
|
||||
@@ -58,7 +58,8 @@
|
||||
"certification_template",
|
||||
"batch_confirmation_template",
|
||||
"column_break_uwsp",
|
||||
"assignment_submission_template"
|
||||
"assignment_submission_template",
|
||||
"payment_reminder_template"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -358,12 +359,18 @@
|
||||
"fieldname": "allow_guest_access",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow Guest Access"
|
||||
},
|
||||
{
|
||||
"fieldname": "payment_reminder_template",
|
||||
"fieldtype": "Link",
|
||||
"label": "Payment Reminder Template",
|
||||
"options": "Email Template"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2025-02-06 11:42:29.803207",
|
||||
"modified": "2025-02-11 11:29:43.412897",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "LMS Settings",
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<div>
|
||||
<p>Hi {{ doc.member_name }},</p>
|
||||
<p>We noticed that you started enrolling in the {{ doc.payment_for_document_type.split(" ")[-1] }} {{ frappe.db.get_value(doc.payment_for_document_type, doc.payment_for_document, "title") }} but didn’t complete your payment.</p>
|
||||
<p>We have a limited number of seats, and they won't be available for long!</p>
|
||||
<p>Don’t miss this opportunity to enhance your skills. Click below to complete your enrollment:</p>
|
||||
<p>
|
||||
<a href="/lms/billing/{{ doc.payment_for_document_type.split(' ')[-1].lower() }}/{{ doc.payment_for_document }}">👉 Complete Your Enrollment</a>
|
||||
</p>
|
||||
<p>If you have any questions or need assistance, feel free to reach out to our support team.</p>
|
||||
<p>Looking forward to seeing you enrolled!</p>
|
||||
</div>
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"attach_print": 0,
|
||||
"channel": "Email",
|
||||
"condition": "doc.payment_received == 0",
|
||||
"creation": "2025-02-03 15:52:32.508093",
|
||||
"date_changed": "creation",
|
||||
"days_in_advance": 1,
|
||||
"docstatus": 0,
|
||||
"doctype": "Notification",
|
||||
"document_type": "LMS Payment",
|
||||
"enabled": 1,
|
||||
"event": "Days After",
|
||||
"idx": 0,
|
||||
"is_standard": 1,
|
||||
"message": "<div>\n <p>Hi {{ doc.member_name }},</p>\n <p>We noticed that you started enrolling in the {{ doc.payment_for_document_type.split(\" \")[-1] }} {{ frappe.db.get_value(doc.payment_for_document_type, doc.payment_for_document, \"title\") }} but didn\u2019t complete your payment.</p>\n <p>We have a limited number of seats, and they won't be available for long!</p>\n <p>Don\u2019t miss this opportunity to enhance your skills. Click below to complete your enrollment:</p>\n <p>\n <a href=\"/lms/billing/{{ doc.payment_for_document_type.split(' ')[-1].lower() }}/{{ doc.payment_for_document }}\">\ud83d\udc49 Complete Your Enrollment</a>\n </p>\n <p>If you have any questions or need assistance, feel free to reach out to our support team.</p>\n <p>Looking forward to seeing you enrolled!</p>\n</div>",
|
||||
"message_type": "HTML",
|
||||
"minutes_offset": 0,
|
||||
"modified": "2025-02-03 16:14:24.568958",
|
||||
"modified_by": "sayali@frappe.io",
|
||||
"module": "LMS",
|
||||
"name": "Payment Completion Reminder",
|
||||
"owner": "sayali@frappe.io",
|
||||
"recipients": [
|
||||
{
|
||||
"receiver_by_document_field": "member"
|
||||
},
|
||||
{
|
||||
"cc": "",
|
||||
"receiver_by_role": "Moderator"
|
||||
}
|
||||
],
|
||||
"send_system_notification": 0,
|
||||
"send_to_all_assignees": 0,
|
||||
"subject": " Complete Your Enrollment - Don't miss out!"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<p>Add your message here</p>
|
||||
@@ -1,6 +0,0 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def get_context(context):
|
||||
# do your magic here
|
||||
pass
|
||||
@@ -1,11 +0,0 @@
|
||||
Hi {{ doc.member_name }},
|
||||
|
||||
We noticed that you started enrolling in the {{ doc.payment_for_document_type.split(" ")[-1] }} {{ frappe.db.get_value(doc.payment_for_document_type, doc.payment_for_document, "title") }} but didn’t complete your payment. We have limited number of seats and they won't be empty for long.
|
||||
|
||||
Don’t miss this opportunity to enhance your skills. Click below to complete your enrollment now:
|
||||
|
||||
[👉 Complete Your Enrollment](/lms/billing/{{ doc.payment_for_document_type.split(" ")[-1].lower()/doc.payment_for_document }})
|
||||
|
||||
If you have any questions or need assistance, feel free to reach out to our support team.
|
||||
|
||||
Looking forward to seeing you enrolled!
|
||||
@@ -1434,7 +1434,7 @@ def get_batch_students(batch):
|
||||
""" Iterate through courses and track their progress """
|
||||
for course in batch_courses:
|
||||
progress = frappe.db.get_value(
|
||||
"LMS Enrollment", {"course": course.course, "member": student.student}, "progress"
|
||||
"LMS Enrollment", {"course": course.course, "member": student.member}, "progress"
|
||||
)
|
||||
detail.courses[course.title] = progress
|
||||
if progress == 100:
|
||||
@@ -1445,11 +1445,12 @@ def get_batch_students(batch):
|
||||
title = frappe.db.get_value(
|
||||
assessment.assessment_type, assessment.assessment_name, "title"
|
||||
)
|
||||
status = has_submitted_assessment(
|
||||
assessment.assessment_name, assessment.assessment_type, student.student
|
||||
assessment_info = has_submitted_assessment(
|
||||
assessment.assessment_name, assessment.assessment_type, student.member
|
||||
)
|
||||
detail.assessments[title] = status
|
||||
if status not in ["Not Attempted", 0]:
|
||||
detail.assessments[title] = assessment_info
|
||||
|
||||
if assessment_info.result == "Passed":
|
||||
assessments_completed += 1
|
||||
|
||||
detail.courses_completed = courses_completed
|
||||
@@ -1493,9 +1494,28 @@ def has_submitted_assessment(assessment, assessment_type, member=None):
|
||||
attempt = frappe.db.exists(doctype, filters)
|
||||
if attempt:
|
||||
attempt_details = frappe.db.get_value(doctype, filters, fields)
|
||||
return attempt_details
|
||||
if assessment_type == "LMS Quiz":
|
||||
result = "Failed"
|
||||
passing_percentage = frappe.db.get_value(
|
||||
"LMS Quiz", assessment, "passing_percentage"
|
||||
)
|
||||
if attempt_details >= passing_percentage:
|
||||
result = "Passed"
|
||||
else:
|
||||
result = attempt_details
|
||||
return frappe._dict(
|
||||
{
|
||||
"status": attempt_details,
|
||||
"result": result,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return not_attempted
|
||||
return frappe._dict(
|
||||
{
|
||||
"status": not_attempted,
|
||||
"result": "Failed",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
||||
@@ -98,4 +98,5 @@ lms.patches.v2_0.update_desk_access_for_lms_roles
|
||||
lms.patches.v2_0.update_quiz_submission_data
|
||||
lms.patches.v2_0.convert_quiz_duration_to_minutes
|
||||
lms.patches.v2_0.allow_guest_access #05-02-2025
|
||||
lms.patches.v2_0.migrate_batch_student_data #10-02-2025
|
||||
lms.patches.v2_0.migrate_batch_student_data #10-02-2025
|
||||
lms.patches.v2_0.delete_old_enrollment_doctypes
|
||||
6
lms/patches/v2_0/delete_old_enrollment_doctypes.py
Normal file
6
lms/patches/v2_0/delete_old_enrollment_doctypes.py
Normal file
@@ -0,0 +1,6 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
frappe.delete_doc("DocType", "Batch Student")
|
||||
frappe.delete_doc("Notification", "Payment Completion Reminder")
|
||||
19
lms/templates/emails/payment_reminder.html
Normal file
19
lms/templates/emails/payment_reminder.html
Normal file
@@ -0,0 +1,19 @@
|
||||
<div>
|
||||
<p>{{ _('Hi') }} {{ billing_name }},</p>
|
||||
<p>{{ _('We noticed that you started enrolling in the') }} {{ type }} {{ title }} {{ _('but didn’t complete your payment') }}.</p>
|
||||
<p>
|
||||
{{ _("We have a limited number of seats, and they won't be available for long!")}}
|
||||
</p>
|
||||
<p>
|
||||
{{ _("Don’t miss this opportunity to enhance your skills. Click below to complete your enrollment") }}:
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ link }}">👉 Complete Your Enrollment</a>
|
||||
</p>
|
||||
<p>
|
||||
{{ _("If you have any questions or need assistance, feel free to reach out to our support team.") }}
|
||||
</p>
|
||||
<p>
|
||||
{{ __("Looking forward to seeing you enrolled!") }}
|
||||
</p>
|
||||
</div>
|
||||
Reference in New Issue
Block a user