chore: resolved conflicts
This commit is contained in:
161
cypress/e2e/batch_creation.cy.js
Normal file
161
cypress/e2e/batch_creation.cy.js
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
describe("Batch Creation", () => {
|
||||||
|
it("creates a new batch", () => {
|
||||||
|
cy.login();
|
||||||
|
cy.wait(500);
|
||||||
|
cy.visit("/lms/batches");
|
||||||
|
|
||||||
|
// Close onboarding modal
|
||||||
|
cy.closeOnboardingModal();
|
||||||
|
|
||||||
|
// Create a batch
|
||||||
|
cy.get("button").contains("New").click();
|
||||||
|
cy.wait(500);
|
||||||
|
cy.url().should("include", "/batches/new/edit");
|
||||||
|
cy.get("label").contains("Title").type("Test Batch");
|
||||||
|
|
||||||
|
cy.get("label").contains("Start Date").type("2030-10-01");
|
||||||
|
cy.get("label").contains("End Date").type("2030-10-31");
|
||||||
|
cy.get("label").contains("Start Time").type("10:00");
|
||||||
|
cy.get("label").contains("End Time").type("11:00");
|
||||||
|
cy.get("label").contains("Timezone").type("IST");
|
||||||
|
cy.get("label").contains("Seat Count").type("10");
|
||||||
|
cy.get("label").contains("Published").click();
|
||||||
|
|
||||||
|
cy.get("label")
|
||||||
|
.contains("Short Description")
|
||||||
|
.type("Test Batch Short Description to test the UI");
|
||||||
|
cy.get("div[contenteditable=true").invoke(
|
||||||
|
"text",
|
||||||
|
"Test Batch Description. I need a very big description to test the UI. This is a very big description. It contains more than once sentence. Its meant to be this long as this is a UI test. Its unbearably long and I'm not sure why I'm typing this much. I'm just going to keep typing until I feel like its long enough. I think its long enough now. I'm going to stop typing now."
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Instructor */
|
||||||
|
cy.get("label")
|
||||||
|
.contains("Instructors")
|
||||||
|
.parent()
|
||||||
|
.within(() => {
|
||||||
|
cy.get("input").click().type("frappe");
|
||||||
|
cy.get("input")
|
||||||
|
.invoke("attr", "aria-controls")
|
||||||
|
.as("instructor_list_id");
|
||||||
|
});
|
||||||
|
cy.get("@instructor_list_id").then((instructor_list_id) => {
|
||||||
|
cy.get(`[id^=${instructor_list_id}`)
|
||||||
|
.should("be.visible")
|
||||||
|
.within(() => {
|
||||||
|
cy.get("[id^=headlessui-combobox-option-").first().click();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.button("Save").click();
|
||||||
|
cy.wait(1000);
|
||||||
|
let batchName;
|
||||||
|
cy.url().then((url) => {
|
||||||
|
console.log(url);
|
||||||
|
batchName = url.split("/").pop();
|
||||||
|
cy.wrap(batchName).as("batchName");
|
||||||
|
});
|
||||||
|
cy.wait(500);
|
||||||
|
|
||||||
|
// Add Student to system
|
||||||
|
|
||||||
|
cy.get("span").contains("Learning").click();
|
||||||
|
cy.get("span").contains("Settings").click();
|
||||||
|
cy.get('[id^="headlessui-dialog-panel-v-"]')
|
||||||
|
.find("span")
|
||||||
|
.contains(/^Members$/)
|
||||||
|
.should("have.text", "Members")
|
||||||
|
.click();
|
||||||
|
cy.get("button").contains("New").click();
|
||||||
|
|
||||||
|
const dateNow = Date.now();
|
||||||
|
const randomEmail = `testuser_${dateNow}@example.com`;
|
||||||
|
const randomName = `Test User ${dateNow}`;
|
||||||
|
|
||||||
|
cy.get("input[placeholder='Email']").type(randomEmail);
|
||||||
|
cy.get("input[placeholder='First Name']").type(randomName);
|
||||||
|
cy.get("button").contains("Add").click();
|
||||||
|
cy.get("div").contains(randomName).should("be.visible").click();
|
||||||
|
|
||||||
|
// View Batch
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.visit("/lms/batches");
|
||||||
|
cy.closeOnboardingModal();
|
||||||
|
|
||||||
|
cy.url().should("include", "/lms/batches");
|
||||||
|
|
||||||
|
cy.get('[id^="headlessui-radiogroup-v-"]')
|
||||||
|
.find("span")
|
||||||
|
.contains("Upcoming")
|
||||||
|
.should("be.visible")
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get("@batchName").then((batchName) => {
|
||||||
|
cy.get(`a[href='/lms/batches/details/${batchName}'`).within(() => {
|
||||||
|
cy.get("div").contains("Test Batch").should("be.visible");
|
||||||
|
cy.get("div")
|
||||||
|
.contains("Test Batch Short Description to test the UI")
|
||||||
|
.should("be.visible");
|
||||||
|
cy.get("span")
|
||||||
|
.contains("01 Oct 2030 - 31 Oct 2030")
|
||||||
|
.should("be.visible");
|
||||||
|
cy.get("span")
|
||||||
|
.contains("10:00 AM - 11:00 AM")
|
||||||
|
.should("be.visible");
|
||||||
|
cy.get("span").contains("IST").should("be.visible");
|
||||||
|
cy.get("a").contains("Frappe").should("be.visible");
|
||||||
|
cy.get("div")
|
||||||
|
.contains("10")
|
||||||
|
.should("be.visible")
|
||||||
|
.get("span")
|
||||||
|
.contains("Seats Left")
|
||||||
|
.should("be.visible");
|
||||||
|
});
|
||||||
|
cy.get(`a[href='/lms/batches/details/${batchName}'`).click();
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get("div").contains("Test Batch").should("be.visible");
|
||||||
|
cy.get("div")
|
||||||
|
.contains("Test Batch Short Description to test the UI")
|
||||||
|
.should("be.visible");
|
||||||
|
cy.get("a").contains("Frappe").should("be.visible");
|
||||||
|
cy.get("span")
|
||||||
|
.contains("01 Oct 2030 - 31 Oct 2030")
|
||||||
|
.should("be.visible");
|
||||||
|
cy.get("span").contains("10:00 AM - 11:00 AM").should("be.visible");
|
||||||
|
cy.get("span").contains("IST").should("be.visible");
|
||||||
|
cy.get("div")
|
||||||
|
.contains("10")
|
||||||
|
.should("be.visible")
|
||||||
|
.get("span")
|
||||||
|
.contains("Seats Left")
|
||||||
|
.should("be.visible");
|
||||||
|
|
||||||
|
cy.get("p")
|
||||||
|
.contains(
|
||||||
|
"Test Batch Description. I need a very big description to test the UI. This is a very big description. It contains more than once sentence. Its meant to be this long as this is a UI test. Its unbearably long and I'm not sure why I'm typing this much. I'm just going to keep typing until I feel like its long enough. I think its long enough now. I'm going to stop typing now."
|
||||||
|
)
|
||||||
|
.should("be.visible");
|
||||||
|
cy.get("button").contains("Manage Batch").click();
|
||||||
|
|
||||||
|
/* Add student to batch */
|
||||||
|
cy.get("button").contains("Add").click();
|
||||||
|
cy.get('div[id^="headlessui-dialog-panel-v-"]')
|
||||||
|
.first()
|
||||||
|
.find("button")
|
||||||
|
.eq(1)
|
||||||
|
.click();
|
||||||
|
cy.get("input[id^='headlessui-combobox-input-v-']").type(randomEmail);
|
||||||
|
cy.get("div").contains(randomEmail).click();
|
||||||
|
cy.get("button").contains("Submit").click();
|
||||||
|
|
||||||
|
// Verify Seat Count
|
||||||
|
cy.get("span").contains("Details").click();
|
||||||
|
cy.get("div")
|
||||||
|
.contains("9")
|
||||||
|
.should("be.visible")
|
||||||
|
.get("span")
|
||||||
|
.contains("Seats Left")
|
||||||
|
.should("be.visible");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -72,8 +72,15 @@ Cypress.Commands.add("paste", { prevSubject: true }, (subject, text) => {
|
|||||||
|
|
||||||
Cypress.Commands.add("closeOnboardingModal", () => {
|
Cypress.Commands.add("closeOnboardingModal", () => {
|
||||||
cy.wait(500);
|
cy.wait(500);
|
||||||
cy.get('[class*="z-50"]')
|
cy.get("body").then(($body) => {
|
||||||
.find('button:has(svg[class*="feather-x"])')
|
// Check if any element with class including 'z-50' exists
|
||||||
.realClick();
|
if ($body.find('[class*="z-50"]').length > 0) {
|
||||||
cy.wait(1000);
|
cy.get('[class*="z-50"]')
|
||||||
|
.find('button:has(svg[class*="feather-x"])')
|
||||||
|
.realClick();
|
||||||
|
cy.wait(1000);
|
||||||
|
} else {
|
||||||
|
cy.log("Onboarding modal not found, skipping close.");
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -106,7 +106,6 @@ const courses = createResource({
|
|||||||
params: {
|
params: {
|
||||||
batch: props.batch,
|
batch: props.batch,
|
||||||
},
|
},
|
||||||
cache: ['batchCourses', props.batchName],
|
|
||||||
auto: true,
|
auto: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ const tabsStructure = computed(() => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Certified Members',
|
label: 'Certified Members',
|
||||||
name: 'certified_participants',
|
name: 'certified_members',
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Tooltip, Button } from 'frappe-ui'
|
import { Tooltip } from 'frappe-ui'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import * as icons from 'lucide-vue-next'
|
import * as icons from 'lucide-vue-next'
|
||||||
|
|||||||
@@ -12,10 +12,7 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</router-link>
|
</router-link>
|
||||||
</header>
|
</header>
|
||||||
<div
|
<div class="mx-auto w-full max-w-4xl pt-6 pb-10">
|
||||||
v-if="participants.data?.length"
|
|
||||||
class="mx-auto w-full max-w-4xl pt-6 pb-10"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col md:flex-row justify-between mb-4 px-3">
|
<div class="flex flex-col md:flex-row justify-between mb-4 px-3">
|
||||||
<div class="text-xl font-semibold text-ink-gray-7 mb-4 md:mb-0">
|
<div class="text-xl font-semibold text-ink-gray-7 mb-4 md:mb-0">
|
||||||
{{ memberCount }} {{ __('certified members') }}
|
{{ memberCount }} {{ __('certified members') }}
|
||||||
@@ -41,7 +38,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="divide-y">
|
<div v-if="participants.data?.length" class="divide-y">
|
||||||
<template v-for="participant in participants.data">
|
<template v-for="participant in participants.data">
|
||||||
<router-link
|
<router-link
|
||||||
:to="{
|
:to="{
|
||||||
@@ -92,6 +89,7 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
<EmptyState v-else type="Certified Members" />
|
||||||
<div
|
<div
|
||||||
v-if="!participants.list.loading && participants.hasNextPage"
|
v-if="!participants.list.loading && participants.hasNextPage"
|
||||||
class="flex justify-center mt-5"
|
class="flex justify-center mt-5"
|
||||||
@@ -101,7 +99,6 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<EmptyState v-else type="Certified Members" />
|
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import {
|
import {
|
||||||
@@ -134,9 +131,8 @@ onMounted(() => {
|
|||||||
const participants = createListResource({
|
const participants = createListResource({
|
||||||
doctype: 'LMS Certificate',
|
doctype: 'LMS Certificate',
|
||||||
url: 'lms.lms.api.get_certified_participants',
|
url: 'lms.lms.api.get_certified_participants',
|
||||||
cache: ['certified_participants'],
|
|
||||||
start: 0,
|
start: 0,
|
||||||
pageLength: 30,
|
pageLength: 100,
|
||||||
})
|
})
|
||||||
|
|
||||||
const getMemberCount = () => {
|
const getMemberCount = () => {
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
</header>
|
</header>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
v-if="jobCount"
|
|
||||||
class="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:items-center justify-between w-full md:w-4/5 mx-auto p-5"
|
class="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:items-center justify-between w-full md:w-4/5 mx-auto p-5"
|
||||||
>
|
>
|
||||||
<div class="text-xl font-semibold text-ink-gray-7 mb-4 md:mb-0">
|
<div class="text-xl font-semibold text-ink-gray-7 mb-4 md:mb-0">
|
||||||
@@ -34,7 +33,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="jobs.data?.length || jobCount > 0"
|
|
||||||
class="grid grid-cols-1 gap-2"
|
class="grid grid-cols-1 gap-2"
|
||||||
:class="user.data ? 'md:grid-cols-3' : 'md:grid-cols-2'"
|
:class="user.data ? 'md:grid-cols-3' : 'md:grid-cols-2'"
|
||||||
>
|
>
|
||||||
@@ -119,12 +117,14 @@ onMounted(() => {
|
|||||||
jobType.value = queries.get('type')
|
jobType.value = queries.get('type')
|
||||||
}
|
}
|
||||||
updateJobs()
|
updateJobs()
|
||||||
getJobCount()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const jobs = createResource({
|
const jobs = createResource({
|
||||||
url: 'lms.lms.api.get_job_opportunities',
|
url: 'lms.lms.api.get_job_opportunities',
|
||||||
cache: ['jobs'],
|
cache: ['jobs'],
|
||||||
|
onSuccess(data) {
|
||||||
|
jobCount.value = data.length
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const updateJobs = () => {
|
const updateJobs = () => {
|
||||||
@@ -165,18 +165,6 @@ const updateFilters = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getJobCount = () => {
|
|
||||||
call('lms.lms.api.get_count', {
|
|
||||||
doctype: 'Job Opportunity',
|
|
||||||
filters: {
|
|
||||||
status: 'Open',
|
|
||||||
disabled: 0,
|
|
||||||
},
|
|
||||||
}).then((data) => {
|
|
||||||
jobCount.value = data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(country, (val) => {
|
watch(country, (val) => {
|
||||||
updateJobs()
|
updateJobs()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ from frappe.utils import (
|
|||||||
date_diff,
|
date_diff,
|
||||||
)
|
)
|
||||||
from frappe.query_builder import DocType
|
from frappe.query_builder import DocType
|
||||||
from pypika.functions import DistinctOptionFunction
|
|
||||||
from lms.lms.utils import get_average_rating, get_lesson_count
|
from lms.lms.utils import get_average_rating, get_lesson_count
|
||||||
from xml.dom.minidom import parseString
|
from xml.dom.minidom import parseString
|
||||||
from lms.lms.doctype.course_lesson.course_lesson import save_progress
|
from lms.lms.doctype.course_lesson.course_lesson import save_progress
|
||||||
@@ -413,7 +412,7 @@ def get_evaluator_details(evaluator):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist(allow_guest=True)
|
@frappe.whitelist(allow_guest=True)
|
||||||
def get_certified_participants(filters=None, start=0, page_length=30):
|
def get_certified_participants(filters=None, start=0, page_length=100):
|
||||||
or_filters = {}
|
or_filters = {}
|
||||||
if not filters:
|
if not filters:
|
||||||
filters = {}
|
filters = {}
|
||||||
@@ -451,18 +450,14 @@ def get_certified_participants(filters=None, start=0, page_length=30):
|
|||||||
return participants
|
return participants
|
||||||
|
|
||||||
|
|
||||||
class CountDistinct(DistinctOptionFunction):
|
|
||||||
def __init__(self, field):
|
|
||||||
super().__init__("COUNT", field, distinct=True)
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist(allow_guest=True)
|
@frappe.whitelist(allow_guest=True)
|
||||||
def get_count_of_certified_members(filters=None):
|
def get_count_of_certified_members(filters=None):
|
||||||
Certificate = DocType("LMS Certificate")
|
Certificate = DocType("LMS Certificate")
|
||||||
|
|
||||||
query = (
|
query = (
|
||||||
frappe.qb.from_(Certificate)
|
frappe.qb.from_(Certificate)
|
||||||
.select(CountDistinct(Certificate.member).as_("total"))
|
.select(Certificate.member)
|
||||||
|
.distinct()
|
||||||
.where(Certificate.published == 1)
|
.where(Certificate.published == 1)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -473,9 +468,11 @@ def get_count_of_certified_members(filters=None):
|
|||||||
Certificate.course_title.like(f"%{value}%")
|
Certificate.course_title.like(f"%{value}%")
|
||||||
| Certificate.batch_title.like(f"%{value}%")
|
| Certificate.batch_title.like(f"%{value}%")
|
||||||
)
|
)
|
||||||
|
elif field == "member_name":
|
||||||
|
query = query.where(Certificate.member_name.like(value[1]))
|
||||||
|
|
||||||
result = query.run(as_dict=True)
|
result = query.run(as_dict=True)
|
||||||
return result[0]["total"] if result else 0
|
return len(result) or 0
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist(allow_guest=True)
|
@frappe.whitelist(allow_guest=True)
|
||||||
@@ -552,7 +549,7 @@ def get_sidebar_settings():
|
|||||||
items = [
|
items = [
|
||||||
"courses",
|
"courses",
|
||||||
"batches",
|
"batches",
|
||||||
"certified_participants",
|
"certified_members",
|
||||||
"jobs",
|
"jobs",
|
||||||
"statistics",
|
"statistics",
|
||||||
"notifications",
|
"notifications",
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
"courses",
|
"courses",
|
||||||
"batches",
|
"batches",
|
||||||
"certified_participants",
|
"certified_participants",
|
||||||
|
"certified_members",
|
||||||
"column_break_exdz",
|
"column_break_exdz",
|
||||||
"jobs",
|
"jobs",
|
||||||
"statistics",
|
"statistics",
|
||||||
@@ -277,6 +278,7 @@
|
|||||||
"default": "1",
|
"default": "1",
|
||||||
"fieldname": "certified_participants",
|
"fieldname": "certified_participants",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
|
"hidden": 1,
|
||||||
"label": "Certified Participants"
|
"label": "Certified Participants"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -397,13 +399,19 @@
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Persona Captured",
|
"label": "Persona Captured",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "certified_members",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Certified Members"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"grid_page_length": 50,
|
"grid_page_length": 50,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2025-05-14 12:43:22.749850",
|
"modified": "2025-05-30 19:02:51.381668",
|
||||||
"modified_by": "sayali@frappe.io",
|
"modified_by": "sayali@frappe.io",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Settings",
|
"name": "LMS Settings",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: frappe\n"
|
"Project-Id-Version: frappe\n"
|
||||||
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
|
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
|
||||||
"POT-Creation-Date: 2025-05-23 16:04+0000\n"
|
"POT-Creation-Date: 2025-05-23 16:04+0000\n"
|
||||||
"PO-Revision-Date: 2025-05-26 18:11\n"
|
"PO-Revision-Date: 2025-06-01 19:38\n"
|
||||||
"Last-Translator: jannat@frappe.io\n"
|
"Last-Translator: jannat@frappe.io\n"
|
||||||
"Language-Team: Persian\n"
|
"Language-Team: Persian\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@@ -2234,7 +2234,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: lms/lms/widgets/CourseCard.html:114
|
#: lms/lms/widgets/CourseCard.html:114
|
||||||
msgid "Free"
|
msgid "Free"
|
||||||
msgstr "رایگان"
|
msgstr "آزاد"
|
||||||
|
|
||||||
#. Option for the 'Type' (Select) field in DocType 'Job Opportunity'
|
#. Option for the 'Type' (Select) field in DocType 'Job Opportunity'
|
||||||
#: frontend/src/pages/Jobs.vue:188
|
#: frontend/src/pages/Jobs.vue:188
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -106,4 +106,5 @@ lms.patches.v2_0.update_job_city_and_country
|
|||||||
lms.patches.v2_0.update_course_evaluator_data
|
lms.patches.v2_0.update_course_evaluator_data
|
||||||
lms.patches.v2_0.move_zoom_settings #20-05-2025
|
lms.patches.v2_0.move_zoom_settings #20-05-2025
|
||||||
lms.patches.v2_0.link_zoom_account_to_live_class
|
lms.patches.v2_0.link_zoom_account_to_live_class
|
||||||
lms.patches.v2_0.link_zoom_account_to_batch
|
lms.patches.v2_0.link_zoom_account_to_batch
|
||||||
|
lms.patches.v2_0.sidebar_for_certified_members
|
||||||
10
lms/patches/v2_0/sidebar_for_certified_members.py
Normal file
10
lms/patches/v2_0/sidebar_for_certified_members.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
show_certified_members = frappe.db.get_single_value(
|
||||||
|
"LMS Settings", "certified_participants"
|
||||||
|
)
|
||||||
|
|
||||||
|
if show_certified_members:
|
||||||
|
frappe.db.set_single_value("LMS Settings", "certified_members", 1)
|
||||||
Reference in New Issue
Block a user