chore: refactor payment settings
This commit is contained in:
@@ -52,7 +52,6 @@ const props = defineProps({
|
|||||||
const saveSettings = createResource({
|
const saveSettings = createResource({
|
||||||
url: 'frappe.client.set_value',
|
url: 'frappe.client.set_value',
|
||||||
makeParams(values) {
|
makeParams(values) {
|
||||||
console.log(values)
|
|
||||||
return {
|
return {
|
||||||
doctype: 'Website Settings',
|
doctype: 'Website Settings',
|
||||||
name: 'Website Settings',
|
name: 'Website Settings',
|
||||||
@@ -77,7 +76,6 @@ const update = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
watch(props.data, (newData) => {
|
watch(props.data, (newData) => {
|
||||||
console.log(newData)
|
|
||||||
if (newData && !isDirty.value) {
|
if (newData && !isDirty.value) {
|
||||||
isDirty.value = true
|
isDirty.value = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,13 @@
|
|||||||
:label="activeTab.label"
|
:label="activeTab.label"
|
||||||
:description="activeTab.description"
|
:description="activeTab.description"
|
||||||
/>
|
/>
|
||||||
|
<PaymentSettings
|
||||||
|
v-else-if="activeTab.label === 'Payment Gateway'"
|
||||||
|
:label="activeTab.label"
|
||||||
|
:description="activeTab.description"
|
||||||
|
:data="data"
|
||||||
|
:fields="activeTab.fields"
|
||||||
|
/>
|
||||||
<BrandSettings
|
<BrandSettings
|
||||||
v-else-if="activeTab.label === 'Branding'"
|
v-else-if="activeTab.label === 'Branding'"
|
||||||
:label="activeTab.label"
|
:label="activeTab.label"
|
||||||
@@ -73,6 +80,7 @@ import SidebarLink from '@/components/SidebarLink.vue'
|
|||||||
import Members from '@/components/Members.vue'
|
import Members from '@/components/Members.vue'
|
||||||
import Categories from '@/components/Categories.vue'
|
import Categories from '@/components/Categories.vue'
|
||||||
import BrandSettings from '@/components/BrandSettings.vue'
|
import BrandSettings from '@/components/BrandSettings.vue'
|
||||||
|
import PaymentSettings from '@/components/PaymentSettings.vue'
|
||||||
|
|
||||||
const show = defineModel()
|
const show = defineModel()
|
||||||
const doctype = ref('LMS Settings')
|
const doctype = ref('LMS Settings')
|
||||||
@@ -133,25 +141,12 @@ const tabsStructure = computed(() => {
|
|||||||
type: 'Link',
|
type: 'Link',
|
||||||
doctype: 'Payment Gateway',
|
doctype: 'Payment Gateway',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: 'Razorpay Key',
|
|
||||||
name: 'razorpay_key',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Razorpay Secret',
|
|
||||||
name: 'razorpay_secret',
|
|
||||||
type: 'password',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: 'Default Currency',
|
label: 'Default Currency',
|
||||||
name: 'default_currency',
|
name: 'default_currency',
|
||||||
type: 'Link',
|
type: 'Link',
|
||||||
doctype: 'Currency',
|
doctype: 'Currency',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: 'Column Break',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: 'Apply GST for India',
|
label: 'Apply GST for India',
|
||||||
name: 'apply_gst',
|
name: 'apply_gst',
|
||||||
@@ -319,24 +314,6 @@ const tabs = computed(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const paymentGateways = computed(() => {
|
|
||||||
const gateways = [
|
|
||||||
'Razorpay',
|
|
||||||
'Stripe',
|
|
||||||
'Mpesa',
|
|
||||||
'Patym',
|
|
||||||
'Paypal',
|
|
||||||
'Braintree',
|
|
||||||
'GoCardless',
|
|
||||||
]
|
|
||||||
return gateways.map((gateway) => {
|
|
||||||
return {
|
|
||||||
label: gateway,
|
|
||||||
value: gateway,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(show, async () => {
|
watch(show, async () => {
|
||||||
if (show.value) {
|
if (show.value) {
|
||||||
const currentTab = await tabs.value
|
const currentTab = await tabs.value
|
||||||
|
|||||||
109
frontend/src/components/PaymentSettings.vue
Normal file
109
frontend/src/components/PaymentSettings.vue
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col min-h-0">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="text-xl font-semibold mb-1">
|
||||||
|
{{ label }}
|
||||||
|
</div>
|
||||||
|
<!-- <Badge
|
||||||
|
v-if="isDirty"
|
||||||
|
:label="__('Not Saved')"
|
||||||
|
variant="subtle"
|
||||||
|
theme="orange"
|
||||||
|
/> -->
|
||||||
|
</div>
|
||||||
|
<div class="overflow-y-scroll">
|
||||||
|
<div class="flex space-x-4">
|
||||||
|
<SettingFields :fields="fields" :data="data.doc" class="w-1/2" />
|
||||||
|
<SettingFields
|
||||||
|
v-if="paymentGateway.data"
|
||||||
|
:fields="paymentGateway.data.fields"
|
||||||
|
:data="paymentGateway.data.data"
|
||||||
|
class="w-1/2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row-reverse mt-auto">
|
||||||
|
<Button variant="solid" @click="update">
|
||||||
|
{{ __('Update') }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import SettingFields from '@/components/SettingFields.vue'
|
||||||
|
import { createResource, Badge, Button } from 'frappe-ui'
|
||||||
|
import { watch, ref } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
fields: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const paymentGateway = createResource({
|
||||||
|
url: 'lms.lms.api.get_payment_gateway_details',
|
||||||
|
makeParams(values) {
|
||||||
|
return {
|
||||||
|
payment_gateway: props.data.doc.payment_gateway,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
auto: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const saveSettings = createResource({
|
||||||
|
url: 'frappe.client.set_value',
|
||||||
|
makeParams(values) {
|
||||||
|
let fields = {}
|
||||||
|
Object.keys(paymentGateway.data.data).forEach((key) => {
|
||||||
|
if (
|
||||||
|
paymentGateway.data.data[key] &&
|
||||||
|
typeof paymentGateway.data.data[key] === 'object'
|
||||||
|
) {
|
||||||
|
fields[key] = paymentGateway.data.data[key].file_url
|
||||||
|
} else {
|
||||||
|
fields[key] = paymentGateway.data.data[key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
doctype: paymentGateway.data.doctype,
|
||||||
|
name: paymentGateway.data.docname,
|
||||||
|
fieldname: fields,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
auto: false,
|
||||||
|
onSuccess(data) {
|
||||||
|
paymentGateway.reload()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const update = () => {
|
||||||
|
props.fields.forEach((f) => {
|
||||||
|
if (f.type != 'Column Break') {
|
||||||
|
props.data.doc[f.name] = f.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
props.data.save.submit()
|
||||||
|
saveSettings.submit()
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.data.doc.payment_gateway,
|
||||||
|
() => {
|
||||||
|
paymentGateway.reload()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</script>
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { FormControl, FileUploader, Button } from 'frappe-ui'
|
import { FormControl, FileUploader, Button } from 'frappe-ui'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { getFileSize } from '@/utils'
|
import { getFileSize, validateFile } from '@/utils'
|
||||||
import { X, FileText } from 'lucide-vue-next'
|
import { X, FileText } from 'lucide-vue-next'
|
||||||
import Link from '@/components/Controls/Link.vue'
|
import Link from '@/components/Controls/Link.vue'
|
||||||
import Codemirror from 'codemirror-editor-vue3'
|
import Codemirror from 'codemirror-editor-vue3'
|
||||||
|
|||||||
@@ -12,21 +12,20 @@
|
|||||||
v-if="access.data?.access && orderSummary.data"
|
v-if="access.data?.access && orderSummary.data"
|
||||||
class="pt-5 pb-10 mx-5"
|
class="pt-5 pb-10 mx-5"
|
||||||
>
|
>
|
||||||
<div class="mb-5">
|
<!-- <div class="mb-5">
|
||||||
<div class="text-lg font-semibold">
|
<div class="text-lg font-semibold">
|
||||||
{{ __('Address') }}
|
{{ __('Address') }}
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="text-sm text-gray-600 mt-1">
|
|
||||||
{{ __('Specify your billing address correctly.') }}
|
|
||||||
</div> -->
|
</div> -->
|
||||||
</div>
|
<div class="flex flex-col lg:flex-row justify-between">
|
||||||
<div class="grid grid-cols-[65%,30%] gap-10">
|
<div
|
||||||
<div class="h-fit bg-gray-100 rounded-md p-5 space-y-4 order-last">
|
class="h-fit bg-gray-100 rounded-md p-5 space-y-4 lg:order-last mb-10 lg:mt-10 text-sm font-medium lg:w-1/4"
|
||||||
<div class="flex items-center justify-between">
|
>
|
||||||
<div class="text-gray-700">
|
<div class="flex items-center justify-between space-x-2">
|
||||||
|
<div class="text-gray-600">
|
||||||
{{ __('Ordered Item') }}
|
{{ __('Ordered Item') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="font-medium">
|
<div class="">
|
||||||
{{ orderSummary.data.title }}
|
{{ orderSummary.data.title }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -34,10 +33,10 @@
|
|||||||
v-if="orderSummary.data.gst_applied"
|
v-if="orderSummary.data.gst_applied"
|
||||||
class="flex items-center justify-between"
|
class="flex items-center justify-between"
|
||||||
>
|
>
|
||||||
<div class="text-gray-700">
|
<div class="text-gray-600">
|
||||||
{{ __('Original Amount') }}
|
{{ __('Original Amount') }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="">
|
||||||
{{ orderSummary.data.original_amount_formatted }}
|
{{ orderSummary.data.original_amount_formatted }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -45,7 +44,7 @@
|
|||||||
v-if="orderSummary.data.gst_applied"
|
v-if="orderSummary.data.gst_applied"
|
||||||
class="flex items-center justify-between mt-2"
|
class="flex items-center justify-between mt-2"
|
||||||
>
|
>
|
||||||
<div class="text-sm text-gray-600 font-medium">
|
<div class="text-gray-600">
|
||||||
{{ __('GST Amount') }}
|
{{ __('GST Amount') }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -53,9 +52,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-between font-semibold border-t border-gray-400 pt-4 mt-2"
|
class="flex items-center justify-between border-t border-gray-400 pt-4 mt-2"
|
||||||
>
|
>
|
||||||
<div>
|
<div class="text-lg font-semibold">
|
||||||
{{ __('Total') }}
|
{{ __('Total') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-lg font-semibold">
|
<div class="text-lg font-semibold">
|
||||||
@@ -64,15 +63,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="flex-1 lg:mr-10">
|
||||||
<!-- <div class="mb-5">
|
<div class="mb-5">
|
||||||
<div class="text-lg font-semibold">
|
<div class="text-lg font-semibold">
|
||||||
{{ __('Address') }}
|
{{ __('Address') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm text-gray-600 mt-1">
|
|
||||||
{{ __('Specify your billing address correctly.') }}
|
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<FormControl
|
<FormControl
|
||||||
@@ -92,14 +88,14 @@
|
|||||||
:label="__('State')"
|
:label="__('State')"
|
||||||
v-model="billingDetails.state"
|
v-model="billingDetails.state"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-4">
|
||||||
<Link
|
<Link
|
||||||
doctype="Country"
|
doctype="Country"
|
||||||
:value="billingDetails.country"
|
:value="billingDetails.country"
|
||||||
@change="(option) => changeCurrency(option)"
|
@change="(option) => changeCurrency(option)"
|
||||||
:label="__('Country')"
|
:label="__('Country')"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div class="space-y-4">
|
|
||||||
<FormControl
|
<FormControl
|
||||||
:label="__('Postal Code')"
|
:label="__('Postal Code')"
|
||||||
v-model="billingDetails.pincode"
|
v-model="billingDetails.pincode"
|
||||||
@@ -200,7 +196,6 @@ const access = createResource({
|
|||||||
const orderSummary = createResource({
|
const orderSummary = createResource({
|
||||||
url: 'lms.lms.utils.get_order_summary',
|
url: 'lms.lms.utils.get_order_summary',
|
||||||
makeParams(values) {
|
makeParams(values) {
|
||||||
console.log(billingDetails.country)
|
|
||||||
return {
|
return {
|
||||||
doctype: props.type == 'course' ? 'LMS Course' : 'LMS Batch',
|
doctype: props.type == 'course' ? 'LMS Course' : 'LMS Batch',
|
||||||
docname: props.name,
|
docname: props.name,
|
||||||
@@ -234,6 +229,7 @@ const paymentLink = createResource({
|
|||||||
return {
|
return {
|
||||||
doctype: props.type == 'course' ? 'LMS Course' : 'LMS Batch',
|
doctype: props.type == 'course' ? 'LMS Course' : 'LMS Batch',
|
||||||
docname: props.name,
|
docname: props.name,
|
||||||
|
title: orderSummary.data.title,
|
||||||
amount: orderSummary.data.original_amount,
|
amount: orderSummary.data.original_amount,
|
||||||
total_amount: orderSummary.data.amount,
|
total_amount: orderSummary.data.amount,
|
||||||
currency: orderSummary.data.currency,
|
currency: orderSummary.data.currency,
|
||||||
@@ -247,6 +243,9 @@ const generatePaymentLink = () => {
|
|||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
validate() {
|
validate() {
|
||||||
|
if (!billingDetails.source) {
|
||||||
|
return __('Please let us know where you heard about us from.')
|
||||||
|
}
|
||||||
return validateAddress()
|
return validateAddress()
|
||||||
},
|
},
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
|
|||||||
@@ -244,7 +244,10 @@ const lesson = createResource({
|
|||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
lessonProgress.value = data.membership?.progress
|
lessonProgress.value = data.membership?.progress
|
||||||
if (data.content) editor.value = renderEditor('editor', data.content)
|
if (data.content) editor.value = renderEditor('editor', data.content)
|
||||||
if (JSON.parse(data.instructor_content)?.blocks?.length > 1)
|
if (
|
||||||
|
data.instructor_content &&
|
||||||
|
JSON.parse(data.instructor_content)?.blocks?.length > 1
|
||||||
|
)
|
||||||
instructorEditor.value = renderEditor(
|
instructorEditor.value = renderEditor(
|
||||||
'instructor-content',
|
'instructor-content',
|
||||||
data.instructor_content
|
data.instructor_content
|
||||||
|
|||||||
@@ -708,3 +708,49 @@ def delete_documents(doctype, documents):
|
|||||||
frappe.only_for("Moderator")
|
frappe.only_for("Moderator")
|
||||||
for doc in documents:
|
for doc in documents:
|
||||||
frappe.delete_doc(doctype, doc)
|
frappe.delete_doc(doctype, doc)
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_payment_gateway_details(payment_gateway):
|
||||||
|
fields = []
|
||||||
|
gateway = frappe.get_doc("Payment Gateway", payment_gateway)
|
||||||
|
|
||||||
|
if gateway.gateway_controller is None:
|
||||||
|
try:
|
||||||
|
data = frappe.get_doc(f"{payment_gateway} Settings").as_dict()
|
||||||
|
meta = frappe.get_meta(f"{payment_gateway} Settings").fields
|
||||||
|
doctype = f"{payment_gateway} Settings"
|
||||||
|
docname = f"{payment_gateway} Settings"
|
||||||
|
except Exception:
|
||||||
|
frappe.throw(_("{0} Settings not found").format(payment_gateway))
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
data = frappe.get_doc(gateway.gateway_settings, gateway.gateway_controller).as_dict()
|
||||||
|
meta = frappe.get_meta(gateway.gateway_settings).fields
|
||||||
|
doctype = gateway.gateway_settings
|
||||||
|
docname = gateway.gateway_controller
|
||||||
|
except Exception:
|
||||||
|
frappe.throw(_("{0} Settings not found").format(payment_gateway))
|
||||||
|
|
||||||
|
for row in meta:
|
||||||
|
if row.fieldtype not in ["Column Break", "Section Break"]:
|
||||||
|
if row.fieldtype in ["Attach", "Attach Image"]:
|
||||||
|
fieldtype = "Upload"
|
||||||
|
data[row.fieldname] = get_file_info(data.get(row.fieldname))
|
||||||
|
else:
|
||||||
|
fieldtype = row.fieldtype
|
||||||
|
|
||||||
|
fields.append(
|
||||||
|
{
|
||||||
|
"label": row.label,
|
||||||
|
"name": row.fieldname,
|
||||||
|
"type": fieldtype,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"fields": fields,
|
||||||
|
"data": data,
|
||||||
|
"doctype": doctype,
|
||||||
|
"docname": docname,
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from lms.lms.utils import (
|
|||||||
get_lesson_url,
|
get_lesson_url,
|
||||||
get_quiz_details,
|
get_quiz_details,
|
||||||
get_assignment_details,
|
get_assignment_details,
|
||||||
|
update_payment_record,
|
||||||
)
|
)
|
||||||
from frappe.email.doctype.email_template.email_template import get_email_template
|
from frappe.email.doctype.email_template.email_template import get_email_template
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ class LMSBatch(Document):
|
|||||||
self.validate_batch_end_date()
|
self.validate_batch_end_date()
|
||||||
self.validate_duplicate_courses()
|
self.validate_duplicate_courses()
|
||||||
self.validate_duplicate_students()
|
self.validate_duplicate_students()
|
||||||
|
self.validate_payments_app()
|
||||||
self.validate_duplicate_assessments()
|
self.validate_duplicate_assessments()
|
||||||
self.validate_membership()
|
self.validate_membership()
|
||||||
self.validate_timetable()
|
self.validate_timetable()
|
||||||
@@ -55,6 +57,12 @@ class LMSBatch(Document):
|
|||||||
_("Course {0} has already been added to this batch.").format(frappe.bold(title))
|
_("Course {0} has already been added to this batch.").format(frappe.bold(title))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def validate_payments_app(self):
|
||||||
|
if self.paid_batch:
|
||||||
|
installed_apps = frappe.get_installed_apps()
|
||||||
|
if "payments" not in installed_apps:
|
||||||
|
frappe.throw(_("Please install the Payments app to create a paid batches."))
|
||||||
|
|
||||||
def validate_duplicate_assessments(self):
|
def validate_duplicate_assessments(self):
|
||||||
assessments = [row.assessment_name for row in self.assessment]
|
assessments = [row.assessment_name for row in self.assessment]
|
||||||
for assessment in self.assessment:
|
for assessment in self.assessment:
|
||||||
@@ -165,64 +173,8 @@ class LMSBatch(Document):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def on_payment_authorized(self, payment_status):
|
def on_payment_authorized(self, payment_status):
|
||||||
if payment_status == "Authorized":
|
if payment_status in ["Authorized", "Completed"]:
|
||||||
self.update_payment_record()
|
update_payment_record("LMS Batch", self.name)
|
||||||
|
|
||||||
def update_payment_record(self):
|
|
||||||
request = frappe.get_all(
|
|
||||||
"Integration Request",
|
|
||||||
{
|
|
||||||
"reference_doctype": self.doctype,
|
|
||||||
"reference_docname": self.name,
|
|
||||||
"owner": frappe.session.user,
|
|
||||||
},
|
|
||||||
order_by="creation desc",
|
|
||||||
limit=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(request):
|
|
||||||
data = frappe.db.get_value("Integration Request", request[0].name, "data")
|
|
||||||
data = frappe._dict(json.loads(data))
|
|
||||||
|
|
||||||
payment_gateway = data.get("payment_gateway")
|
|
||||||
frappe.db.set_value(
|
|
||||||
"LMS Payment",
|
|
||||||
data.payment,
|
|
||||||
{
|
|
||||||
"payment_received": 1,
|
|
||||||
"payment_id": data[f"{payment_gateway.lower()}_payment_id"],
|
|
||||||
"order_id": data["order_id"],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
enroll_in_batch(data.payment, self)
|
|
||||||
except Exception as e:
|
|
||||||
frappe.log_error(frappe.get_traceback(), _("Enrollment Failed"))
|
|
||||||
|
|
||||||
|
|
||||||
def enroll_in_batch(payment_name, batch):
|
|
||||||
if not frappe.db.exists(
|
|
||||||
"Batch Student", {"parent": batch.name, "student": frappe.session.user}
|
|
||||||
):
|
|
||||||
student = frappe.new_doc("Batch Student")
|
|
||||||
current_count = frappe.db.count("Batch Student", {"parent": batch.name})
|
|
||||||
payment = frappe.db.get_value(
|
|
||||||
"LMS Payment", payment_name, ["name", "source"], as_dict=True
|
|
||||||
)
|
|
||||||
|
|
||||||
student.update(
|
|
||||||
{
|
|
||||||
"student": frappe.session.user,
|
|
||||||
"payment": payment.name,
|
|
||||||
"source": payment.source,
|
|
||||||
"parent": batch.name,
|
|
||||||
"parenttype": "LMS Batch",
|
|
||||||
"parentfield": "students",
|
|
||||||
"idx": current_count + 1,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
student.save(ignore_permissions=True)
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from frappe.model.document import Document
|
|||||||
from frappe.utils import cint, today
|
from frappe.utils import cint, today
|
||||||
from frappe.utils.telemetry import capture
|
from frappe.utils.telemetry import capture
|
||||||
from lms.lms.utils import get_chapters, can_create_courses
|
from lms.lms.utils import get_chapters, can_create_courses
|
||||||
from ...utils import generate_slug, validate_image
|
from ...utils import generate_slug, validate_image, update_payment_record
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
|
||||||
|
|
||||||
@@ -18,6 +18,7 @@ class LMSCourse(Document):
|
|||||||
self.validate_instructors()
|
self.validate_instructors()
|
||||||
self.validate_video_link()
|
self.validate_video_link()
|
||||||
self.validate_status()
|
self.validate_status()
|
||||||
|
self.validate_payments_app()
|
||||||
self.image = validate_image(self.image)
|
self.image = validate_image(self.image)
|
||||||
|
|
||||||
def validate_published(self):
|
def validate_published(self):
|
||||||
@@ -44,10 +45,20 @@ class LMSCourse(Document):
|
|||||||
if self.published:
|
if self.published:
|
||||||
self.status = "Approved"
|
self.status = "Approved"
|
||||||
|
|
||||||
|
def validate_payments_app(self):
|
||||||
|
if self.paid_course:
|
||||||
|
installed_apps = frappe.get_installed_apps()
|
||||||
|
if "payments" not in installed_apps:
|
||||||
|
frappe.throw(_("Please install the Payments app to create a paid courses."))
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
if not self.upcoming and self.has_value_changed("upcoming"):
|
if not self.upcoming and self.has_value_changed("upcoming"):
|
||||||
self.send_email_to_interested_users()
|
self.send_email_to_interested_users()
|
||||||
|
|
||||||
|
def on_payment_authorized(self, payment_status):
|
||||||
|
if payment_status in ["Authorized", "Completed"]:
|
||||||
|
update_payment_record("LMS Course", self.name)
|
||||||
|
|
||||||
def send_email_to_interested_users(self):
|
def send_email_to_interested_users(self):
|
||||||
interested_users = frappe.get_all(
|
interested_users = frappe.get_all(
|
||||||
"LMS Course Interest", {"course": self.name}, ["name", "user"]
|
"LMS Course Interest", {"course": self.name}, ["name", "user"]
|
||||||
|
|||||||
@@ -43,14 +43,12 @@
|
|||||||
"payment_settings_tab",
|
"payment_settings_tab",
|
||||||
"payment_section",
|
"payment_section",
|
||||||
"payment_gateway",
|
"payment_gateway",
|
||||||
"razorpay_key",
|
|
||||||
"razorpay_secret",
|
|
||||||
"apply_gst",
|
|
||||||
"column_break_cfcv",
|
|
||||||
"default_currency",
|
"default_currency",
|
||||||
|
"exception_country",
|
||||||
|
"column_break_cfcv",
|
||||||
|
"apply_gst",
|
||||||
"show_usd_equivalent",
|
"show_usd_equivalent",
|
||||||
"apply_rounding",
|
"apply_rounding",
|
||||||
"exception_country",
|
|
||||||
"email_templates_tab",
|
"email_templates_tab",
|
||||||
"certification_template",
|
"certification_template",
|
||||||
"batch_confirmation_template",
|
"batch_confirmation_template",
|
||||||
@@ -148,16 +146,6 @@
|
|||||||
"fieldname": "column_break_cfcv",
|
"fieldname": "column_break_cfcv",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "razorpay_key",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"label": "Razorpay Key"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "razorpay_secret",
|
|
||||||
"fieldtype": "Password",
|
|
||||||
"label": "Razorpay Secret"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "apply_gst",
|
"fieldname": "apply_gst",
|
||||||
@@ -174,7 +162,7 @@
|
|||||||
"depends_on": "show_usd_equivalent",
|
"depends_on": "show_usd_equivalent",
|
||||||
"fieldname": "exception_country",
|
"fieldname": "exception_country",
|
||||||
"fieldtype": "Table MultiSelect",
|
"fieldtype": "Table MultiSelect",
|
||||||
"label": "Maintain Original Currency",
|
"label": "Primary Countries",
|
||||||
"options": "Payment Country"
|
"options": "Payment Country"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -343,7 +331,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-09-24 19:55:47.914930",
|
"modified": "2024-09-30 18:30:28.689084",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Settings",
|
"name": "LMS Settings",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ def validate_currency(payment_gateway, currency):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_payment_link(doctype, docname, amount, total_amount, currency, address):
|
def get_payment_link(doctype, docname, title, amount, total_amount, currency, address):
|
||||||
payment_gateway = get_payment_gateway()
|
payment_gateway = get_payment_gateway()
|
||||||
address = frappe._dict(address)
|
address = frappe._dict(address)
|
||||||
amount_with_gst = total_amount if total_amount != amount else 0
|
amount_with_gst = total_amount if total_amount != amount else 0
|
||||||
@@ -24,26 +24,24 @@ def get_payment_link(doctype, docname, amount, total_amount, currency, address):
|
|||||||
payment = record_payment(address, doctype, docname, amount, currency, amount_with_gst)
|
payment = record_payment(address, doctype, docname, amount, currency, amount_with_gst)
|
||||||
controller = get_controller(payment_gateway)
|
controller = get_controller(payment_gateway)
|
||||||
|
|
||||||
if controller.doctype == "Stripe Settings":
|
if doctype == "LMS Course":
|
||||||
print(controller.as_dict())
|
redirect_to = f"/lms/courses/{docname}/learn/1-1"
|
||||||
doctype = "Stripe Settings"
|
elif doctype == "LMS Batch":
|
||||||
docname = controller.name
|
redirect_to = f"/lms/batches/{docname}"
|
||||||
|
|
||||||
payment_details = {
|
payment_details = {
|
||||||
"amount": total_amount,
|
"amount": total_amount,
|
||||||
"title": f"Payment for {doctype} {docname}",
|
"title": f"Payment for {doctype} {title} {docname}",
|
||||||
"description": f"{address.billing_name}'s payment for {doctype} {docname}",
|
"description": f"{address.billing_name}'s payment for {title}",
|
||||||
"reference_doctype": doctype,
|
"reference_doctype": doctype,
|
||||||
"reference_docname": docname,
|
"reference_docname": docname,
|
||||||
"payer_email": frappe.session.user,
|
"payer_email": frappe.session.user,
|
||||||
"payer_name": address.billing_name,
|
"payer_name": address.billing_name,
|
||||||
"order_id": docname,
|
|
||||||
"currency": currency,
|
"currency": currency,
|
||||||
"payment_gateway": payment_gateway,
|
"payment_gateway": payment_gateway,
|
||||||
"redirect_to": f"/lms/batches/{docname}",
|
"redirect_to": redirect_to,
|
||||||
"payment": payment.name,
|
"payment": payment.name,
|
||||||
}
|
}
|
||||||
print(controller)
|
|
||||||
url = controller.get_payment_url(**payment_details)
|
url = controller.get_payment_url(**payment_details)
|
||||||
|
|
||||||
return url
|
return url
|
||||||
|
|||||||
@@ -1682,12 +1682,10 @@ def get_order_summary(doctype, docname, country=None):
|
|||||||
)
|
)
|
||||||
details.original_amount = details.amount
|
details.original_amount = details.amount
|
||||||
details.original_amount_formatted = fmt_money(details.amount, 0, details.currency)
|
details.original_amount_formatted = fmt_money(details.amount, 0, details.currency)
|
||||||
print(details.currency)
|
|
||||||
if details.currency == "INR":
|
if details.currency == "INR":
|
||||||
print("inside")
|
details.amount, details.gst_applied = apply_gst(details.amount, country)
|
||||||
details.amount, details.gst_applied = apply_gst(details.amount)
|
|
||||||
details.gst_amount_formatted = fmt_money(details.gst_applied, 0, details.currency)
|
details.gst_amount_formatted = fmt_money(details.gst_applied, 0, details.currency)
|
||||||
print(details.amount, details.gst_applied, details.gst_amount_formatted)
|
|
||||||
|
|
||||||
details.total_amount_formatted = fmt_money(details.amount, 0, details.currency)
|
details.total_amount_formatted = fmt_money(details.amount, 0, details.currency)
|
||||||
return details
|
return details
|
||||||
@@ -1744,3 +1742,89 @@ def publish_notifications(doc, method):
|
|||||||
frappe.publish_realtime(
|
frappe.publish_realtime(
|
||||||
"publish_lms_notifications", user=doc.for_user, after_commit=True
|
"publish_lms_notifications", user=doc.for_user, after_commit=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def update_payment_record(doctype, docname):
|
||||||
|
request = frappe.get_all(
|
||||||
|
"Integration Request",
|
||||||
|
{
|
||||||
|
"reference_doctype": doctype,
|
||||||
|
"reference_docname": docname,
|
||||||
|
"owner": frappe.session.user,
|
||||||
|
},
|
||||||
|
order_by="creation desc",
|
||||||
|
limit=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(request):
|
||||||
|
data = frappe.db.get_value("Integration Request", request[0].name, "data")
|
||||||
|
data = frappe._dict(json.loads(data))
|
||||||
|
|
||||||
|
payment_gateway = data.get("payment_gateway")
|
||||||
|
if payment_gateway == "Razorpay":
|
||||||
|
payment_id = "razorpay_payment_id"
|
||||||
|
elif "Stripe" in payment_gateway:
|
||||||
|
payment_id = "stripe_token_id"
|
||||||
|
else:
|
||||||
|
payment_id = "order_id"
|
||||||
|
|
||||||
|
frappe.db.set_value(
|
||||||
|
"LMS Payment",
|
||||||
|
data.payment,
|
||||||
|
{
|
||||||
|
"payment_received": 1,
|
||||||
|
"payment_id": data.get(payment_id),
|
||||||
|
"order_id": data.get("order_id"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if doctype == "LMS Course":
|
||||||
|
enroll_in_course(data.payment, docname)
|
||||||
|
else:
|
||||||
|
enroll_in_batch(data.payment, docname)
|
||||||
|
except Exception as e:
|
||||||
|
frappe.log_error(frappe.get_traceback(), _("Enrollment Failed"))
|
||||||
|
|
||||||
|
|
||||||
|
def enroll_in_course(payment_name, course):
|
||||||
|
if not frappe.db.exists(
|
||||||
|
"LMS Enrollment", {"member": frappe.session.user, "course": course}
|
||||||
|
):
|
||||||
|
enrollment = frappe.new_doc("LMS Enrollment")
|
||||||
|
payment = frappe.db.get_value(
|
||||||
|
"LMS Payment", payment_name, ["name", "source"], as_dict=True
|
||||||
|
)
|
||||||
|
|
||||||
|
enrollment.update(
|
||||||
|
{
|
||||||
|
"member": frappe.session.user,
|
||||||
|
"course": course,
|
||||||
|
"payment": payment.name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
enrollment.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
|
||||||
|
def enroll_in_batch(payment_name, batch):
|
||||||
|
if not frappe.db.exists(
|
||||||
|
"Batch Student", {"parent": batch, "student": frappe.session.user}
|
||||||
|
):
|
||||||
|
student = frappe.new_doc("Batch Student")
|
||||||
|
current_count = frappe.db.count("Batch Student", {"parent": batch})
|
||||||
|
payment = frappe.db.get_value(
|
||||||
|
"LMS Payment", payment_name, ["name", "source"], as_dict=True
|
||||||
|
)
|
||||||
|
|
||||||
|
student.update(
|
||||||
|
{
|
||||||
|
"student": frappe.session.user,
|
||||||
|
"payment": payment.name,
|
||||||
|
"source": payment.source,
|
||||||
|
"parent": batch,
|
||||||
|
"parenttype": "LMS Batch",
|
||||||
|
"parentfield": "students",
|
||||||
|
"idx": current_count + 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
student.save(ignore_permissions=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user