mirror of
https://github.com/frappe/lms.git
synced 2026-05-02 13:39:31 +03:00
Merge pull request #2167 from raizasafeel/fix/payment
fix: use backend field metadata for billing and transaction forms
This commit is contained in:
@@ -55,17 +55,18 @@
|
||||
:label="__('Member')"
|
||||
doctype="User"
|
||||
v-model="transactionData.member"
|
||||
:required="true"
|
||||
:required="!!fieldMeta.member?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
:label="__('Billing Name')"
|
||||
v-model="transactionData.billing_name"
|
||||
:required="true"
|
||||
:required="!!fieldMeta.billing_name?.reqd"
|
||||
/>
|
||||
<Link
|
||||
:label="__('Source')"
|
||||
v-model="transactionData.source"
|
||||
doctype="LMS Source"
|
||||
:required="!!fieldMeta.source?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
type="select"
|
||||
@@ -73,12 +74,14 @@
|
||||
:label="__('Payment For Document Type')"
|
||||
v-model="transactionData.payment_for_document_type"
|
||||
doctype="DocType"
|
||||
:required="!!fieldMeta.payment_for_document_type?.reqd"
|
||||
/>
|
||||
<Link
|
||||
v-if="transactionData.payment_for_document_type"
|
||||
:label="__('Payment For Document')"
|
||||
v-model="transactionData.payment_for_document"
|
||||
:doctype="transactionData.payment_for_document_type"
|
||||
:required="!!fieldMeta.payment_for_document?.reqd"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -90,17 +93,18 @@
|
||||
:label="__('Currency')"
|
||||
v-model="transactionData.currency"
|
||||
doctype="Currency"
|
||||
:required="true"
|
||||
:required="!!fieldMeta.currency?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
:label="__('Amount')"
|
||||
v-model="transactionData.amount"
|
||||
:required="true"
|
||||
:required="!!fieldMeta.amount?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
v-if="transactionData.amount_with_gst"
|
||||
:label="__('Amount with GST')"
|
||||
v-model="transactionData.amount_with_gst"
|
||||
:required="!!fieldMeta.amount_with_gst?.reqd"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -113,21 +117,25 @@
|
||||
v-if="transactionData.coupon"
|
||||
:label="__('Coupon Code')"
|
||||
v-model="transactionData.coupon"
|
||||
:required="!!fieldMeta.coupon?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
v-if="transactionData.coupon"
|
||||
:label="__('Coupon Code')"
|
||||
v-model="transactionData.coupon_code"
|
||||
:required="!!fieldMeta.coupon_code?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
v-if="transactionData.coupon"
|
||||
:label="__('Discount Amount')"
|
||||
v-model="transactionData.discount_amount"
|
||||
:required="!!fieldMeta.discount_amount?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
v-if="transactionData.coupon"
|
||||
:label="__('Original Amount')"
|
||||
v-model="transactionData.original_amount"
|
||||
:required="!!fieldMeta.original_amount?.reqd"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -140,17 +148,27 @@
|
||||
:label="__('Address')"
|
||||
v-model="transactionData.address"
|
||||
doctype="Address"
|
||||
:required="true"
|
||||
:required="!!fieldMeta.address?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
:label="__('GSTIN')"
|
||||
v-model="transactionData.gstin"
|
||||
:required="!!fieldMeta.gstin?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
:label="__('PAN')"
|
||||
v-model="transactionData.pan"
|
||||
:required="!!fieldMeta.pan?.reqd"
|
||||
/>
|
||||
<FormControl :label="__('GSTIN')" v-model="transactionData.gstin" />
|
||||
<FormControl :label="__('PAN')" v-model="transactionData.pan" />
|
||||
<FormControl
|
||||
:label="__('Payment ID')"
|
||||
v-model="transactionData.payment_id"
|
||||
:required="!!fieldMeta.payment_id?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
:label="__('Order ID')"
|
||||
v-model="transactionData.order_id"
|
||||
:required="!!fieldMeta.order_id?.reqd"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -171,6 +189,10 @@ const show = defineModel('show')
|
||||
const props = defineProps<{
|
||||
transactions: any
|
||||
data: any
|
||||
fieldMeta: Record<
|
||||
string,
|
||||
{ reqd?: number; default?: string; description?: string }
|
||||
>
|
||||
}>()
|
||||
|
||||
const saveTransaction = () => {
|
||||
@@ -211,48 +233,49 @@ const updateTransaction = () => {
|
||||
}
|
||||
|
||||
const openDetails = () => {
|
||||
if (props.data) {
|
||||
const docType = props.data.payment_for_document_type
|
||||
const docName = props.data.payment_for_document
|
||||
if (docType && docName) {
|
||||
router.push({
|
||||
name: docType == 'LMS Course' ? 'CourseDetail' : 'BatchDetail',
|
||||
params: {
|
||||
[docType == 'LMS Course' ? 'courseName' : 'batchName']: docName,
|
||||
},
|
||||
})
|
||||
}
|
||||
const docType = transactionData.value?.payment_for_document_type
|
||||
const docName = transactionData.value?.payment_for_document
|
||||
if (docType && docName) {
|
||||
router.push({
|
||||
name: docType == 'LMS Course' ? 'CourseDetail' : 'BatchDetail',
|
||||
params: {
|
||||
[docType == 'LMS Course' ? 'courseName' : 'batchName']: docName,
|
||||
},
|
||||
})
|
||||
show.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const emptyTransactionData = {
|
||||
const getDefault = (fieldname: string) =>
|
||||
props.fieldMeta[fieldname]?.default || null
|
||||
|
||||
const getEmptyTransactionData = () => ({
|
||||
payment_received: false,
|
||||
payment_for_certificate: false,
|
||||
member: null,
|
||||
billing_name: null,
|
||||
source: null,
|
||||
payment_for_document_type: null,
|
||||
payment_for_document: null,
|
||||
member: getDefault('member'),
|
||||
billing_name: getDefault('billing_name'),
|
||||
source: getDefault('source'),
|
||||
payment_for_document_type: getDefault('payment_for_document_type'),
|
||||
payment_for_document: getDefault('payment_for_document'),
|
||||
member_consent: false,
|
||||
currency: null,
|
||||
amount: null,
|
||||
amount_with_gst: null,
|
||||
coupon: null,
|
||||
coupon_code: null,
|
||||
discount_amount: null,
|
||||
original_amount: null,
|
||||
order_id: null,
|
||||
payment_id: null,
|
||||
gstin: null,
|
||||
pan: null,
|
||||
address: null,
|
||||
}
|
||||
currency: getDefault('currency'),
|
||||
amount: getDefault('amount'),
|
||||
amount_with_gst: getDefault('amount_with_gst'),
|
||||
coupon: getDefault('coupon'),
|
||||
coupon_code: getDefault('coupon_code'),
|
||||
discount_amount: getDefault('discount_amount'),
|
||||
original_amount: getDefault('original_amount'),
|
||||
order_id: getDefault('order_id'),
|
||||
payment_id: getDefault('payment_id'),
|
||||
gstin: getDefault('gstin'),
|
||||
pan: getDefault('pan'),
|
||||
address: getDefault('address'),
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
(newVal) => {
|
||||
transactionData.value = newVal ? { ...newVal } : emptyTransactionData
|
||||
transactionData.value = newVal ? { ...newVal } : getEmptyTransactionData()
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
v-if="step == 'new'"
|
||||
:transactions="transactions"
|
||||
:data="data"
|
||||
:fieldMeta="fieldMeta.data || {}"
|
||||
v-model:show="show"
|
||||
@updateStep="updateStep"
|
||||
/>
|
||||
@@ -17,13 +18,14 @@
|
||||
v-else-if="step == 'details'"
|
||||
:transactions="transactions"
|
||||
:data="data"
|
||||
:fieldMeta="fieldMeta.data || {}"
|
||||
v-model:show="show"
|
||||
@updateStep="updateStep"
|
||||
/>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { createListResource } from 'frappe-ui'
|
||||
import { createListResource, createResource } from 'frappe-ui'
|
||||
import TransactionList from '@/components/Settings/Transactions/TransactionList.vue'
|
||||
import TransactionDetails from '@/components/Settings/Transactions/TransactionDetails.vue'
|
||||
|
||||
@@ -45,6 +47,11 @@ const updateStep = (newStep: 'list' | 'new' | 'edit', newData: any) => {
|
||||
}
|
||||
}
|
||||
|
||||
const fieldMeta = createResource({
|
||||
url: 'lms.lms.api.get_payment_field_meta',
|
||||
auto: true,
|
||||
})
|
||||
|
||||
const transactions = createListResource({
|
||||
doctype: 'LMS Payment',
|
||||
fields: [
|
||||
|
||||
@@ -114,25 +114,27 @@
|
||||
<FormControl
|
||||
:label="__('Billing Name')"
|
||||
v-model="billingDetails.billing_name"
|
||||
:required="true"
|
||||
:required="!!fieldMeta.billing_name?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
:label="__('Address Line 1')"
|
||||
v-model="billingDetails.address_line1"
|
||||
:required="true"
|
||||
:required="!!fieldMeta.address_line1?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
:label="__('Address Line 2')"
|
||||
v-model="billingDetails.address_line2"
|
||||
:required="!!fieldMeta.address_line2?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
:label="__('City')"
|
||||
v-model="billingDetails.city"
|
||||
:required="true"
|
||||
:required="!!fieldMeta.city?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
:label="__('State/Province')"
|
||||
v-model="billingDetails.state"
|
||||
:required="!!fieldMeta.state?.reqd"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
@@ -141,34 +143,36 @@
|
||||
:value="billingDetails.country"
|
||||
@change="(option) => changeCurrency(option)"
|
||||
:label="__('Country')"
|
||||
:required="true"
|
||||
:required="!!fieldMeta.country?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
:label="__('Postal Code')"
|
||||
v-model="billingDetails.pincode"
|
||||
:required="true"
|
||||
:required="!!fieldMeta.pincode?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
:label="__('Phone Number')"
|
||||
v-model="billingDetails.phone"
|
||||
:required="true"
|
||||
:required="!!fieldMeta.phone?.reqd"
|
||||
/>
|
||||
<Link
|
||||
doctype="LMS Source"
|
||||
:value="billingDetails.source"
|
||||
@change="(option) => (billingDetails.source = option)"
|
||||
:label="__('Where did you hear about us?')"
|
||||
:required="true"
|
||||
:required="!!fieldMeta.source?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
v-if="billingDetails.country == 'India'"
|
||||
:label="__('GST Number')"
|
||||
v-model="billingDetails.gstin"
|
||||
:required="!!fieldMeta.gstin?.reqd"
|
||||
/>
|
||||
<FormControl
|
||||
v-if="billingDetails.country == 'India'"
|
||||
:label="__('PAN Number')"
|
||||
v-model="billingDetails.pan"
|
||||
:required="!!fieldMeta.pan?.reqd"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -273,6 +277,7 @@ const access = createResource({
|
||||
name: props.name,
|
||||
},
|
||||
onSuccess(data) {
|
||||
Object.assign(fieldMeta, data.billing_field_meta || {})
|
||||
setBillingDetails(data.address)
|
||||
orderSummary.submit()
|
||||
},
|
||||
@@ -295,19 +300,24 @@ const orderSummary = createResource({
|
||||
|
||||
const appliedCoupon = ref(null)
|
||||
const billingDetails = reactive({})
|
||||
const fieldMeta = reactive({})
|
||||
|
||||
const getDefault = (fieldname) => fieldMeta[fieldname]?.default || ''
|
||||
|
||||
const setBillingDetails = (data) => {
|
||||
billingDetails.billing_name = data?.billing_name || ''
|
||||
billingDetails.address_line1 = data?.address_line1 || ''
|
||||
billingDetails.address_line2 = data?.address_line2 || ''
|
||||
billingDetails.city = data?.city || ''
|
||||
billingDetails.state = data?.state || ''
|
||||
billingDetails.country = data?.country || ''
|
||||
billingDetails.pincode = data?.pincode || ''
|
||||
billingDetails.phone = data?.phone || ''
|
||||
billingDetails.source = data?.source || ''
|
||||
billingDetails.gstin = data?.gstin || ''
|
||||
billingDetails.pan = data?.pan || ''
|
||||
billingDetails.billing_name = data?.billing_name || getDefault('billing_name')
|
||||
billingDetails.address_line1 =
|
||||
data?.address_line1 || getDefault('address_line1')
|
||||
billingDetails.address_line2 =
|
||||
data?.address_line2 || getDefault('address_line2')
|
||||
billingDetails.city = data?.city || getDefault('city')
|
||||
billingDetails.state = data?.state || getDefault('state')
|
||||
billingDetails.country = data?.country || getDefault('country')
|
||||
billingDetails.pincode = data?.pincode || getDefault('pincode')
|
||||
billingDetails.phone = data?.phone || getDefault('phone')
|
||||
billingDetails.source = data?.source || getDefault('source')
|
||||
billingDetails.gstin = data?.gstin || getDefault('gstin')
|
||||
billingDetails.pan = data?.pan || getDefault('pan')
|
||||
}
|
||||
|
||||
const paymentLink = createResource({
|
||||
@@ -336,7 +346,7 @@ const generatePaymentLink = () => {
|
||||
{},
|
||||
{
|
||||
validate() {
|
||||
if (!billingDetails.source) {
|
||||
if (!billingDetails.source && fieldMeta.source?.reqd) {
|
||||
return __('Please let us know where you heard about us from.')
|
||||
}
|
||||
if (!billingDetails.member_consent) {
|
||||
@@ -370,15 +380,19 @@ function removeCoupon() {
|
||||
}
|
||||
|
||||
const validateAddress = () => {
|
||||
let mandatoryFields = [
|
||||
let billingFields = [
|
||||
'billing_name',
|
||||
'address_line1',
|
||||
'address_line2',
|
||||
'city',
|
||||
'state',
|
||||
'pincode',
|
||||
'country',
|
||||
'phone',
|
||||
'source',
|
||||
'gstin',
|
||||
'pan',
|
||||
]
|
||||
let mandatoryFields = billingFields.filter((f) => fieldMeta[f]?.reqd)
|
||||
for (let field of mandatoryFields) {
|
||||
if (!billingDetails[field])
|
||||
return (
|
||||
|
||||
+49
-1
@@ -37,6 +37,7 @@ from lms.lms.utils import (
|
||||
get_average_rating,
|
||||
get_batch_details,
|
||||
get_course_details,
|
||||
get_field_meta,
|
||||
get_instructors,
|
||||
get_lesson_count,
|
||||
get_lms_route,
|
||||
@@ -103,7 +104,54 @@ def validate_billing_access(billing_type: str, name: str):
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
return {"access": access, "message": message, "address": address}
|
||||
payment_fields = get_payment_field_meta()
|
||||
address_fields = get_field_meta(
|
||||
"Address",
|
||||
[
|
||||
"address_line1",
|
||||
"address_line2",
|
||||
"city",
|
||||
"state",
|
||||
"country",
|
||||
"pincode",
|
||||
"phone",
|
||||
],
|
||||
)
|
||||
billing_field_meta = {**payment_fields, **address_fields}
|
||||
|
||||
return {
|
||||
"access": access,
|
||||
"message": message,
|
||||
"address": address,
|
||||
"billing_field_meta": billing_field_meta,
|
||||
}
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_payment_field_meta():
|
||||
return get_field_meta(
|
||||
"LMS Payment",
|
||||
[
|
||||
"member",
|
||||
"billing_name",
|
||||
"source",
|
||||
"payment_for_document_type",
|
||||
"payment_for_document",
|
||||
"currency",
|
||||
"amount",
|
||||
"amount_with_gst",
|
||||
"original_amount",
|
||||
"discount_amount",
|
||||
"coupon",
|
||||
"coupon_code",
|
||||
"address",
|
||||
"gstin",
|
||||
"pan",
|
||||
"payment_id",
|
||||
"order_id",
|
||||
"member_consent",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def verify_billing_access(doctype, name, billing_type):
|
||||
|
||||
@@ -2328,3 +2328,20 @@ def recalculate_course_progress(course: str, member: str):
|
||||
)
|
||||
frappe.db.set_value("LMS Enrollment", membership, "progress", progress)
|
||||
update_program_progress(member)
|
||||
|
||||
|
||||
def get_field_meta(doctype, fieldnames):
|
||||
"""Returns field metadata for 'fieldnames' from 'doctype'"""
|
||||
meta = frappe.get_meta(doctype)
|
||||
fieldnames_meta = {}
|
||||
|
||||
for fieldname in fieldnames:
|
||||
field = meta.get_field(fieldname)
|
||||
if field:
|
||||
fieldnames_meta[fieldname] = {
|
||||
"reqd": field.reqd,
|
||||
"default": field.default,
|
||||
"description": field.description,
|
||||
}
|
||||
|
||||
return fieldnames_meta
|
||||
|
||||
Reference in New Issue
Block a user