Merge pull request #1726 from pateljannat/payment-settings-refactor
refactor: payment settings
This commit is contained in:
6
frontend/components.d.ts
vendored
6
frontend/components.d.ts
vendored
@@ -86,7 +86,11 @@ declare module 'vue' {
|
|||||||
Notes: typeof import('./src/components/Notes/Notes.vue')['default']
|
Notes: typeof import('./src/components/Notes/Notes.vue')['default']
|
||||||
NotPermitted: typeof import('./src/components/NotPermitted.vue')['default']
|
NotPermitted: typeof import('./src/components/NotPermitted.vue')['default']
|
||||||
PageModal: typeof import('./src/components/Modals/PageModal.vue')['default']
|
PageModal: typeof import('./src/components/Modals/PageModal.vue')['default']
|
||||||
|
PaymentGateway: typeof import('./src/components/Settings/PaymentGateway.vue')['default']
|
||||||
|
PaymentGatewayDetails: typeof import('./src/components/Settings/PaymentGatewayDetails.vue')['default']
|
||||||
|
PaymentGateways: typeof import('./src/components/Settings/PaymentGateways.vue')['default']
|
||||||
PaymentSettings: typeof import('./src/components/Settings/PaymentSettings.vue')['default']
|
PaymentSettings: typeof import('./src/components/Settings/PaymentSettings.vue')['default']
|
||||||
|
PaymentTransactions: typeof import('./src/components/Settings/PaymentTransactions.vue')['default']
|
||||||
Play: typeof import('./src/components/Icons/Play.vue')['default']
|
Play: typeof import('./src/components/Icons/Play.vue')['default']
|
||||||
ProgressBar: typeof import('./src/components/ProgressBar.vue')['default']
|
ProgressBar: typeof import('./src/components/ProgressBar.vue')['default']
|
||||||
Question: typeof import('./src/components/Modals/Question.vue')['default']
|
Question: typeof import('./src/components/Modals/Question.vue')['default']
|
||||||
@@ -105,6 +109,8 @@ declare module 'vue' {
|
|||||||
StudentHeatmap: typeof import('./src/components/StudentHeatmap.vue')['default']
|
StudentHeatmap: typeof import('./src/components/StudentHeatmap.vue')['default']
|
||||||
StudentModal: typeof import('./src/components/Modals/StudentModal.vue')['default']
|
StudentModal: typeof import('./src/components/Modals/StudentModal.vue')['default']
|
||||||
Tags: typeof import('./src/components/Tags.vue')['default']
|
Tags: typeof import('./src/components/Tags.vue')['default']
|
||||||
|
TransactionDetails: typeof import('./src/components/Settings/TransactionDetails.vue')['default']
|
||||||
|
Transactions: typeof import('./src/components/Settings/Transactions.vue')['default']
|
||||||
UnsplashImageBrowser: typeof import('./src/components/UnsplashImageBrowser.vue')['default']
|
UnsplashImageBrowser: typeof import('./src/components/UnsplashImageBrowser.vue')['default']
|
||||||
UpcomingEvaluations: typeof import('./src/components/UpcomingEvaluations.vue')['default']
|
UpcomingEvaluations: typeof import('./src/components/UpcomingEvaluations.vue')['default']
|
||||||
Uploader: typeof import('./src/components/Controls/Uploader.vue')['default']
|
Uploader: typeof import('./src/components/Controls/Uploader.vue')['default']
|
||||||
|
|||||||
@@ -54,6 +54,7 @@
|
|||||||
"@vitejs/plugin-vue": "^5.0.3",
|
"@vitejs/plugin-vue": "^5.0.3",
|
||||||
"autoprefixer": "^10.4.2",
|
"autoprefixer": "^10.4.2",
|
||||||
"postcss": "^8.4.5",
|
"postcss": "^8.4.5",
|
||||||
"vite": "^5.0.11"
|
"vite": "^5.0.11",
|
||||||
|
"vite-plugin-pwa": "^1.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,9 +141,6 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
show: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const evaluators = createListResource({
|
const evaluators = createListResource({
|
||||||
|
|||||||
@@ -156,9 +156,6 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
show: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const members = createResource({
|
const members = createResource({
|
||||||
@@ -185,7 +182,6 @@ const openProfile = (username: string) => {
|
|||||||
username: username,
|
username: username,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
console.log(show.value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const newMember = createResource({
|
const newMember = createResource({
|
||||||
|
|||||||
233
frontend/src/components/Settings/PaymentGatewayDetails.vue
Normal file
233
frontend/src/components/Settings/PaymentGatewayDetails.vue
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog
|
||||||
|
v-model="show"
|
||||||
|
:options="{
|
||||||
|
title:
|
||||||
|
gatewayID === 'new'
|
||||||
|
? __('New Payment Gateway')
|
||||||
|
: __('Edit Payment Gateway'),
|
||||||
|
size: '3xl',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #body-content>
|
||||||
|
<SettingFields
|
||||||
|
v-if="gatewayID != 'new' && paymentGateway.data"
|
||||||
|
:fields="paymentGateway.data.fields"
|
||||||
|
:data="paymentGateway.data.data"
|
||||||
|
class="pt-5 my-0"
|
||||||
|
/>
|
||||||
|
<div v-else>
|
||||||
|
<FormControl
|
||||||
|
v-model="newGateway"
|
||||||
|
:label="__('Select Payment Gateway')"
|
||||||
|
type="select"
|
||||||
|
:options="allGatewayOptions"
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
<SettingFields
|
||||||
|
v-if="newGateway"
|
||||||
|
:fields="newGatewayFields"
|
||||||
|
:data="newGatewayData"
|
||||||
|
class="pt-5 my-0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ close }">
|
||||||
|
<div class="pb-5 float-right">
|
||||||
|
<Button variant="solid" @click="saveSettings(close)">
|
||||||
|
{{ __('Save') }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
call,
|
||||||
|
createListResource,
|
||||||
|
createResource,
|
||||||
|
Dialog,
|
||||||
|
FormControl,
|
||||||
|
} from 'frappe-ui'
|
||||||
|
import { computed, ref, watch } from 'vue'
|
||||||
|
import SettingFields from '@/components/Settings/SettingFields.vue'
|
||||||
|
|
||||||
|
const show = defineModel<boolean>({ required: true, default: false })
|
||||||
|
const paymentGateways = defineModel<any>('paymentGateways')
|
||||||
|
const newGateway = ref(null)
|
||||||
|
const newGatewayFields = ref([])
|
||||||
|
const newGatewayData = ref<Record<string, any>>({})
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
gatewayID: string | null
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const paymentGateway = createResource({
|
||||||
|
url: 'lms.lms.api.get_payment_gateway_details',
|
||||||
|
makeParams(values: any) {
|
||||||
|
return {
|
||||||
|
payment_gateway: props.gatewayID,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transform(data: any) {
|
||||||
|
arrangeFields(data.fields)
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const allGateways = createListResource({
|
||||||
|
doctype: 'DocType',
|
||||||
|
filters: {
|
||||||
|
module: 'Payment Gateways',
|
||||||
|
},
|
||||||
|
fields: ['name', 'issingle'],
|
||||||
|
})
|
||||||
|
|
||||||
|
const gatewayFields = createResource({
|
||||||
|
url: 'lms.lms.api.get_new_gateway_fields',
|
||||||
|
makeParams(values: any) {
|
||||||
|
return {
|
||||||
|
doctype: values.doctype,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const arrangeFields = (fields: any[]) => {
|
||||||
|
fields = fields.sort((a, b) => {
|
||||||
|
if (a.type === 'Upload' && b.type !== 'Upload') {
|
||||||
|
return 1
|
||||||
|
} else if (a.type !== 'Upload' && b.type === 'Upload') {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
|
fields.splice(3, 0, {
|
||||||
|
type: 'Column Break',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.gatewayID,
|
||||||
|
() => {
|
||||||
|
if (props.gatewayID && props.gatewayID !== 'new') {
|
||||||
|
paymentGateway.reload()
|
||||||
|
} else if (props.gatewayID == 'new') {
|
||||||
|
allGateways.reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const getNewGateway = () => {
|
||||||
|
return allGateways.data?.find((gateway: any) =>
|
||||||
|
gateway.name.includes(newGateway.value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(newGateway, () => {
|
||||||
|
let gatewayDoc = getNewGateway()
|
||||||
|
gatewayFields.reload({ doctype: gatewayDoc.name }).then(() => {
|
||||||
|
let fields = gatewayFields.data || []
|
||||||
|
arrangeFields(fields)
|
||||||
|
newGatewayFields.value = fields
|
||||||
|
prepareGatewayData()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const saveSettings = (close: () => void) => {
|
||||||
|
if (props.gatewayID === 'new') {
|
||||||
|
saveNewGateway(close)
|
||||||
|
} else {
|
||||||
|
saveExistingGateway(
|
||||||
|
paymentGateway.data.doctype,
|
||||||
|
paymentGateway.data.docname,
|
||||||
|
close
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveNewGateway = (close: () => void) => {
|
||||||
|
let gatewayDoc = getNewGateway()
|
||||||
|
if (gatewayDoc.issingle) {
|
||||||
|
saveExistingGateway(gatewayDoc.name, gatewayDoc.name, close)
|
||||||
|
} else {
|
||||||
|
call('frappe.client.insert', {
|
||||||
|
doc: {
|
||||||
|
doctype: gatewayDoc.name,
|
||||||
|
...newGatewayData.value,
|
||||||
|
},
|
||||||
|
}).then((data: any) => {
|
||||||
|
paymentGateways.value.reload()
|
||||||
|
close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveExistingGateway = (
|
||||||
|
doctype: string,
|
||||||
|
docname: string,
|
||||||
|
close: () => void
|
||||||
|
) => {
|
||||||
|
call('frappe.client.set_value', {
|
||||||
|
doctype: doctype,
|
||||||
|
name: docname,
|
||||||
|
fieldname: getGatewayFields(),
|
||||||
|
}).then(() => {
|
||||||
|
paymentGateways.value?.reload()
|
||||||
|
close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getGatewayFields = () => {
|
||||||
|
let data =
|
||||||
|
props.gatewayID == 'new' ? newGatewayData.value : paymentGateway.data.data
|
||||||
|
return Object.keys(data).reduce((fields: any, key: string) => {
|
||||||
|
if (data[key] && typeof data[key] === 'object') {
|
||||||
|
fields[key] = data[key].file_url
|
||||||
|
} else {
|
||||||
|
fields[key] = data[key]
|
||||||
|
}
|
||||||
|
return fields
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
const createGatewayRecord = (gatewayDoc: any, data: any = {}) => {
|
||||||
|
call('frappe.client.insert', {
|
||||||
|
doc: {
|
||||||
|
doctype: 'Payment Gateway',
|
||||||
|
gateway: newGateway.value,
|
||||||
|
gateway_controller: gatewayDoc.issingle ? '' : gatewayDoc.name,
|
||||||
|
gateway_settings: gatewayDoc.issingle ? '' : data.name,
|
||||||
|
},
|
||||||
|
}).then(() => {
|
||||||
|
paymentGateways.value?.reload()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const allGatewayOptions = computed(() => {
|
||||||
|
let options: string[] = []
|
||||||
|
let gatewayList = allGateways.data?.map((gateway: any) => gateway.name) || []
|
||||||
|
gatewayList.forEach((gateway: any) => {
|
||||||
|
let gatewayName = gateway.split(' ')[0]
|
||||||
|
let existingGateways =
|
||||||
|
paymentGateways.value?.data?.map((pg: any) => pg.name) || []
|
||||||
|
if (
|
||||||
|
!options.includes(gatewayName) &&
|
||||||
|
!existingGateways.includes(gatewayName)
|
||||||
|
) {
|
||||||
|
options.push(gatewayName)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return options.map((gateway: string) => ({ label: gateway, value: gateway }))
|
||||||
|
})
|
||||||
|
|
||||||
|
const prepareGatewayData = () => {
|
||||||
|
newGatewayData.value = {}
|
||||||
|
if (newGatewayFields.value.length) {
|
||||||
|
newGatewayFields.value.forEach((field: any) => {
|
||||||
|
newGatewayData.value[field.fieldname] = field.default || ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
140
frontend/src/components/Settings/PaymentGateways.vue
Normal file
140
frontend/src/components/Settings/PaymentGateways.vue
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex min-h-0 flex-col text-base">
|
||||||
|
<div class="flex items-center justify-between mb-5">
|
||||||
|
<div>
|
||||||
|
<div class="text-xl font-semibold mb-1 text-ink-gray-9">
|
||||||
|
{{ __(label) }}
|
||||||
|
</div>
|
||||||
|
<div class="text-ink-gray-6 leading-5">
|
||||||
|
{{ __(description) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button @click="openForm('new')">
|
||||||
|
<template #prefix>
|
||||||
|
<Plus class="h-3 w-3 stroke-1.5" />
|
||||||
|
</template>
|
||||||
|
{{ __('New') }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="paymentGateways.data?.length" class="overflow-y-scroll">
|
||||||
|
<ListView
|
||||||
|
:columns="columns"
|
||||||
|
:rows="paymentGateways.data"
|
||||||
|
row-key="name"
|
||||||
|
:options="{
|
||||||
|
showTooltip: false,
|
||||||
|
onRowClick: (row) => {
|
||||||
|
openForm(row.name)
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<ListHeader
|
||||||
|
class="mb-2 grid items-center space-x-4 rounded bg-surface-gray-2 p-2"
|
||||||
|
>
|
||||||
|
<ListHeaderItem :item="item" v-for="item in columns">
|
||||||
|
<template #prefix="{ item }">
|
||||||
|
<FeatherIcon
|
||||||
|
v-if="item.icon"
|
||||||
|
:name="item.icon"
|
||||||
|
class="h-4 w-4 stroke-1.5"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</ListHeaderItem>
|
||||||
|
</ListHeader>
|
||||||
|
|
||||||
|
<ListRows>
|
||||||
|
<ListRow :row="row" v-for="row in paymentGateways.data">
|
||||||
|
<template #default="{ column, item }">
|
||||||
|
<ListRowItem :item="row[column.key]" :align="column.align">
|
||||||
|
<div v-if="column.key == 'enabled'">
|
||||||
|
<Badge v-if="row[column.key]" theme="green">
|
||||||
|
{{ __('Enabled') }}
|
||||||
|
</Badge>
|
||||||
|
<Badge v-else theme="gray">
|
||||||
|
{{ __('Disabled') }}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<div v-else class="leading-5 text-sm">
|
||||||
|
{{ row[column.key] }}
|
||||||
|
</div>
|
||||||
|
</ListRowItem>
|
||||||
|
</template>
|
||||||
|
</ListRow>
|
||||||
|
</ListRows>
|
||||||
|
|
||||||
|
<ListSelectBanner>
|
||||||
|
<template #actions="{ unselectAll, selections }">
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
@click="removeAccount(selections, unselectAll)"
|
||||||
|
>
|
||||||
|
<Trash2 class="h-4 w-4 stroke-1.5" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ListSelectBanner>
|
||||||
|
</ListView>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<PaymentGatewayDetails
|
||||||
|
v-model="showForm"
|
||||||
|
:gatewayID="currentGateway"
|
||||||
|
v-model:paymentGateways="paymentGateways"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
Badge,
|
||||||
|
Button,
|
||||||
|
createListResource,
|
||||||
|
FeatherIcon,
|
||||||
|
ListView,
|
||||||
|
ListHeader,
|
||||||
|
ListHeaderItem,
|
||||||
|
ListRows,
|
||||||
|
ListRow,
|
||||||
|
ListRowItem,
|
||||||
|
ListSelectBanner,
|
||||||
|
} from 'frappe-ui'
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
import { Plus, Trash2 } from 'lucide-vue-next'
|
||||||
|
import PaymentGatewayDetails from '@/components/Settings/PaymentGatewayDetails.vue'
|
||||||
|
|
||||||
|
const showForm = ref(false)
|
||||||
|
const currentGateway = ref(null)
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const paymentGateways = createListResource({
|
||||||
|
doctype: 'Payment Gateway',
|
||||||
|
fields: ['name', 'gateway_settings', 'gateway_controller'],
|
||||||
|
auto: true,
|
||||||
|
orderBy: 'modified desc',
|
||||||
|
})
|
||||||
|
|
||||||
|
const openForm = (gatewayID) => {
|
||||||
|
currentGateway.value = gatewayID
|
||||||
|
showForm.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = computed(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: __('Gateway'),
|
||||||
|
key: 'name',
|
||||||
|
icon: 'credit-card',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col h-full">
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<div class="text-xl font-semibold mb-1 text-ink-gray-9">
|
|
||||||
{{ label }}
|
|
||||||
</div>
|
|
||||||
<!-- <Badge
|
|
||||||
v-if="isDirty"
|
|
||||||
:label="__('Not Saved')"
|
|
||||||
variant="subtle"
|
|
||||||
theme="orange"
|
|
||||||
/> -->
|
|
||||||
</div>
|
|
||||||
<div class="overflow-y-scroll">
|
|
||||||
<div class="flex flex-col divide-y">
|
|
||||||
<SettingFields :fields="fields" :data="data.doc" />
|
|
||||||
<SettingFields
|
|
||||||
v-if="paymentGateway.data"
|
|
||||||
:fields="paymentGateway.data.fields"
|
|
||||||
:data="paymentGateway.data.data"
|
|
||||||
class="pt-5 my-0"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-row-reverse mt-auto">
|
|
||||||
<Button variant="solid" @click="update">
|
|
||||||
{{ __('Update') }}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup>
|
|
||||||
import SettingFields from '@/components/Settings/SettingFields.vue'
|
|
||||||
import { createResource, Badge, Button } from 'frappe-ui'
|
|
||||||
import { watch } 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,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
transform(data) {
|
|
||||||
arrangeFields(data.fields)
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
auto: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
const arrangeFields = (fields) => {
|
|
||||||
fields = fields.sort((a, b) => {
|
|
||||||
if (a.type === 'Upload' && b.type !== 'Upload') {
|
|
||||||
return 1
|
|
||||||
} else if (a.type !== 'Upload' && b.type === 'Upload') {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
})
|
|
||||||
|
|
||||||
fields.splice(3, 0, {
|
|
||||||
type: 'Column Break',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
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>
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col justify-between h-full">
|
<div class="flex flex-col justify-between h-full text-base">
|
||||||
<div>
|
<div>
|
||||||
<div class="flex itemsc-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="text-xl font-semibold leading-none mb-1 text-ink-gray-9">
|
<div class="text-xl font-semibold leading-none mb-2 text-ink-gray-9">
|
||||||
{{ __(label) }}
|
{{ __(label) }}
|
||||||
</div>
|
</div>
|
||||||
<Badge
|
<Badge
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
theme="orange"
|
theme="orange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs text-ink-gray-5">
|
<div class="text-ink-gray-6 leading-5">
|
||||||
{{ __(description) }}
|
{{ __(description) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -30,12 +30,9 @@
|
|||||||
</CodeEditor>
|
</CodeEditor>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div v-else-if="field.type == 'Upload'">
|
||||||
v-else-if="field.type == 'Upload'"
|
<div class="space-y-1 mb-2">
|
||||||
class="grid grid-cols-2 gap-10"
|
<div class="text-sm text-ink-gray-5 font-medium">
|
||||||
>
|
|
||||||
<div class="space-y-2">
|
|
||||||
<div class="text-sm text-ink-gray-8 font-medium mb-1">
|
|
||||||
{{ __(field.label) }}
|
{{ __(field.label) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm text-ink-gray-5 leading-5">
|
<div class="text-sm text-ink-gray-5 leading-5">
|
||||||
|
|||||||
@@ -37,22 +37,26 @@
|
|||||||
<component
|
<component
|
||||||
v-if="activeTab.template"
|
v-if="activeTab.template"
|
||||||
:is="activeTab.template"
|
:is="activeTab.template"
|
||||||
v-model:show="show"
|
|
||||||
v-bind="{
|
v-bind="{
|
||||||
label: activeTab.label,
|
label: activeTab.label,
|
||||||
description: activeTab.description,
|
description: activeTab.description,
|
||||||
...(activeTab.label === 'Branding'
|
...(activeTab.label == 'Branding'
|
||||||
? { fields: activeTab.fields }
|
? { fields: activeTab.fields }
|
||||||
: {}),
|
: {}),
|
||||||
|
...(activeTab.label == 'Evaluators' ||
|
||||||
|
activeTab.label == 'Members' ||
|
||||||
|
activeTab.label == 'Transactions'
|
||||||
|
? { 'onUpdate:show': (val) => (show = val), show }
|
||||||
|
: {}),
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<PaymentSettings
|
<!-- <PaymentSettings
|
||||||
v-else-if="activeTab.label === 'Payment Gateway'"
|
v-else-if="activeTab.label === 'Gateways'"
|
||||||
:label="activeTab.label"
|
:label="activeTab.label"
|
||||||
:description="activeTab.description"
|
:description="activeTab.description"
|
||||||
:data="data"
|
:data="data"
|
||||||
:fields="activeTab.fields"
|
:fields="activeTab.fields"
|
||||||
/>
|
/> -->
|
||||||
<SettingDetails
|
<SettingDetails
|
||||||
v-else
|
v-else
|
||||||
:fields="activeTab.fields"
|
:fields="activeTab.fields"
|
||||||
@@ -76,7 +80,8 @@ import Evaluators from '@/components/Settings/Evaluators.vue'
|
|||||||
import Categories from '@/components/Settings/Categories.vue'
|
import Categories from '@/components/Settings/Categories.vue'
|
||||||
import EmailTemplates from '@/components/Settings/EmailTemplates.vue'
|
import EmailTemplates from '@/components/Settings/EmailTemplates.vue'
|
||||||
import BrandSettings from '@/components/Settings/BrandSettings.vue'
|
import BrandSettings from '@/components/Settings/BrandSettings.vue'
|
||||||
import PaymentSettings from '@/components/Settings/PaymentSettings.vue'
|
import PaymentGateways from '@/components/Settings/PaymentGateways.vue'
|
||||||
|
import Transactions from '@/components/Settings/Transactions.vue'
|
||||||
import ZoomSettings from '@/components/Settings/ZoomSettings.vue'
|
import ZoomSettings from '@/components/Settings/ZoomSettings.vue'
|
||||||
import Badges from '@/components/Settings/Badges.vue'
|
import Badges from '@/components/Settings/Badges.vue'
|
||||||
|
|
||||||
@@ -159,14 +164,13 @@ const tabsStructure = computed(() => {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Settings',
|
label: 'Payment',
|
||||||
hideLabel: true,
|
hideLabel: false,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: 'Payment Gateway',
|
label: 'Configuration',
|
||||||
icon: 'DollarSign',
|
icon: 'CreditCard',
|
||||||
description:
|
description: 'Manage all your payment related settings and defaults',
|
||||||
'Configure the payment gateway and other payment related settings',
|
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
label: 'Default Currency',
|
label: 'Default Currency',
|
||||||
@@ -200,6 +204,18 @@ const tabsStructure = computed(() => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Gateways',
|
||||||
|
icon: 'DollarSign',
|
||||||
|
template: markRaw(PaymentGateways),
|
||||||
|
description: 'Add and manage all your payment gateways',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Transactions',
|
||||||
|
icon: 'Landmark',
|
||||||
|
template: markRaw(Transactions),
|
||||||
|
description: 'View all your payment transactions',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -275,7 +291,7 @@ const tabsStructure = computed(() => {
|
|||||||
name: 'favicon',
|
name: 'favicon',
|
||||||
type: 'Upload',
|
type: 'Upload',
|
||||||
description:
|
description:
|
||||||
'Appears in the browser tab next to the page title, bookmarks, and shortcuts to help users quickly identify the application.',
|
'Appears in the browser tab next to the page title to help users quickly identify the application.',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
152
frontend/src/components/Settings/TransactionDetails.vue
Normal file
152
frontend/src/components/Settings/TransactionDetails.vue
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog
|
||||||
|
v-model="show"
|
||||||
|
:options="{
|
||||||
|
title: __('Transaction Details'),
|
||||||
|
size: '3xl',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #body-content>
|
||||||
|
<div v-if="transactionData" class="text-base">
|
||||||
|
<div class="grid grid-cols-3 gap-5 mt-5">
|
||||||
|
<FormControl
|
||||||
|
:label="__('Payment Received')"
|
||||||
|
type="checkbox"
|
||||||
|
v-model="transactionData.payment_received"
|
||||||
|
/>
|
||||||
|
<FormControl
|
||||||
|
:label="__('Payment For Certificate')"
|
||||||
|
type="checkbox"
|
||||||
|
v-model="transactionData.payment_for_certificate"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-3 gap-5 mt-5">
|
||||||
|
<Link
|
||||||
|
:label="__('Member')"
|
||||||
|
doctype="User"
|
||||||
|
v-model="transactionData.member"
|
||||||
|
/>
|
||||||
|
<FormControl
|
||||||
|
:label="__('Billing Name')"
|
||||||
|
v-model="transactionData.billing_name"
|
||||||
|
/>
|
||||||
|
<Link
|
||||||
|
:label="__('Source')"
|
||||||
|
v-model="transactionData.source"
|
||||||
|
doctype="LMS Source"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="font-semibold mt-10">
|
||||||
|
{{ __('Payment Details') }}
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-3 gap-5 mt-5">
|
||||||
|
<Link
|
||||||
|
:label="__('Payment For Document Type')"
|
||||||
|
v-model="transactionData.payment_for_document_type"
|
||||||
|
doctype="DocType"
|
||||||
|
/>
|
||||||
|
<Link
|
||||||
|
:label="__('Payment For Document')"
|
||||||
|
v-model="transactionData.payment_for_document"
|
||||||
|
:doctype="transactionData.payment_for_document_type"
|
||||||
|
/>
|
||||||
|
<Link
|
||||||
|
:label="__('Address')"
|
||||||
|
v-model="transactionData.address"
|
||||||
|
doctype="Address"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-3 gap-5 mt-5">
|
||||||
|
<Link
|
||||||
|
:label="__('Currency')"
|
||||||
|
v-model="transactionData.currency"
|
||||||
|
doctype="Currency"
|
||||||
|
/>
|
||||||
|
<FormControl :label="__('Amount')" v-model="transactionData.amount" />
|
||||||
|
<FormControl
|
||||||
|
:label="__('Order ID')"
|
||||||
|
v-model="transactionData.order_id"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-3 gap-5 mt-5">
|
||||||
|
<FormControl :label="__('GSTIN')" v-model="transactionData.gstin" />
|
||||||
|
<FormControl :label="__('PAN')" v-model="transactionData.pan" />
|
||||||
|
<FormControl
|
||||||
|
:label="__('Payment ID')"
|
||||||
|
v-model="transactionData.payment_id"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ close }">
|
||||||
|
<div class="space-x-2 pb-5 float-right">
|
||||||
|
<Button @click="openDetails(close)">
|
||||||
|
{{ __('Open the ') }}
|
||||||
|
{{
|
||||||
|
transaction.payment_for_document_type == 'LMS Course'
|
||||||
|
? __('Course')
|
||||||
|
: __('Batch')
|
||||||
|
}}
|
||||||
|
</Button>
|
||||||
|
<Button variant="solid" @click="saveTransaction(close)">
|
||||||
|
{{ __('Save') }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Dialog, FormControl, Button } from 'frappe-ui'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
import Link from '@/components/Controls/Link.vue'
|
||||||
|
|
||||||
|
const show = defineModel<boolean>({ required: true, default: false })
|
||||||
|
const transactions = defineModel<any>('transactions')
|
||||||
|
const router = useRouter()
|
||||||
|
const showModal = defineModel('show')
|
||||||
|
const transactionData = ref<{ [key: string]: any } | null>(null)
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
transaction: { [key: string]: any } | null
|
||||||
|
}>()
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.transaction,
|
||||||
|
(newVal) => {
|
||||||
|
transactionData.value = newVal ? { ...newVal } : null
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
const saveTransaction = (close: () => void) => {
|
||||||
|
transactions.value.setValue
|
||||||
|
.submit({
|
||||||
|
...transactionData.value,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const openDetails = (close: Function) => {
|
||||||
|
if (props.transaction) {
|
||||||
|
const docType = props.transaction.payment_for_document_type
|
||||||
|
const docName = props.transaction.payment_for_document
|
||||||
|
if (docType && docName) {
|
||||||
|
router.push({
|
||||||
|
name: docType == 'LMS Course' ? 'CourseDetail' : 'BatchDetail',
|
||||||
|
params: {
|
||||||
|
[docType == 'LMS Course' ? 'courseName' : 'batchName']: docName,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close()
|
||||||
|
showModal.value = false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
241
frontend/src/components/Settings/Transactions.vue
Normal file
241
frontend/src/components/Settings/Transactions.vue
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex min-h-0 flex-col text-base">
|
||||||
|
<div class="mb-5">
|
||||||
|
<div class="text-xl font-semibold mb-1 text-ink-gray-9">
|
||||||
|
{{ __(label) }}
|
||||||
|
</div>
|
||||||
|
<div class="text-ink-gray-6 leading-5">
|
||||||
|
{{ __(description) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center space-x-5 mb-4">
|
||||||
|
<FormControl
|
||||||
|
v-model="billingName"
|
||||||
|
:placeholder="__('Filter by Billing Name')"
|
||||||
|
/>
|
||||||
|
<Link
|
||||||
|
v-model="member"
|
||||||
|
doctype="User"
|
||||||
|
:placeholder="__('Filter by Member')"
|
||||||
|
/>
|
||||||
|
<FormControl
|
||||||
|
v-model="paymentReceived"
|
||||||
|
type="checkbox"
|
||||||
|
:label="__('Payment Received')"
|
||||||
|
/>
|
||||||
|
<FormControl
|
||||||
|
v-model="paymentForCertificate"
|
||||||
|
type="checkbox"
|
||||||
|
:label="__('Payment for Certificate')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="transactions.data?.length" class="overflow-y-scroll">
|
||||||
|
<ListView
|
||||||
|
:columns="columns"
|
||||||
|
:rows="transactions.data"
|
||||||
|
row-key="name"
|
||||||
|
:options="{
|
||||||
|
showTooltip: false,
|
||||||
|
selectable: false,
|
||||||
|
onRowClick: (row: { [key: string]: any }) => {
|
||||||
|
openForm(row)
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<ListHeader
|
||||||
|
class="mb-2 grid items-center space-x-4 rounded bg-surface-gray-2 p-2"
|
||||||
|
>
|
||||||
|
<ListHeaderItem :item="item" v-for="item in columns">
|
||||||
|
<template #prefix="{ item }">
|
||||||
|
<FeatherIcon
|
||||||
|
v-if="item.icon"
|
||||||
|
:name="item.icon"
|
||||||
|
class="h-4 w-4 stroke-1.5"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</ListHeaderItem>
|
||||||
|
</ListHeader>
|
||||||
|
|
||||||
|
<ListRows>
|
||||||
|
<ListRow :row="row" v-for="row in transactions.data">
|
||||||
|
<template #default="{ column, item }">
|
||||||
|
<ListRowItem :item="row[column.key]" :align="column.align">
|
||||||
|
<FormControl
|
||||||
|
v-if="
|
||||||
|
['payment_received', 'payment_for_certificate'].includes(
|
||||||
|
column.key
|
||||||
|
)
|
||||||
|
"
|
||||||
|
type="checkbox"
|
||||||
|
v-model="row[column.key]"
|
||||||
|
:disabled="true"
|
||||||
|
/>
|
||||||
|
<div v-else-if="column.key == 'amount'">
|
||||||
|
{{ getCurrencySymbol(row['currency']) }} {{ row[column.key] }}
|
||||||
|
</div>
|
||||||
|
<div v-else class="leading-5 text-sm">
|
||||||
|
{{ row[column.key] }}
|
||||||
|
</div>
|
||||||
|
</ListRowItem>
|
||||||
|
</template>
|
||||||
|
</ListRow>
|
||||||
|
</ListRows>
|
||||||
|
</ListView>
|
||||||
|
<div
|
||||||
|
v-if="transactions.data.length && transactions.hasNextPage"
|
||||||
|
class="flex justify-center mt-4"
|
||||||
|
>
|
||||||
|
<Button @click="transactions.next()">
|
||||||
|
<template #prefix>
|
||||||
|
<RefreshCw class="h-3 w-3 stroke-1.5" />
|
||||||
|
</template>
|
||||||
|
{{ __('Load More') }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<TransactionDetails
|
||||||
|
v-model="showForm"
|
||||||
|
:transaction="currentTransaction"
|
||||||
|
v-model:transactions="transactions"
|
||||||
|
v-model:show="show"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
createListResource,
|
||||||
|
ListView,
|
||||||
|
ListHeader,
|
||||||
|
ListHeaderItem,
|
||||||
|
FeatherIcon,
|
||||||
|
ListRows,
|
||||||
|
ListRow,
|
||||||
|
ListRowItem,
|
||||||
|
FormControl,
|
||||||
|
} from 'frappe-ui'
|
||||||
|
import { computed, ref, watch } from 'vue'
|
||||||
|
import { RefreshCw } from 'lucide-vue-next'
|
||||||
|
import TransactionDetails from './TransactionDetails.vue'
|
||||||
|
import Link from '@/components/Controls/Link.vue'
|
||||||
|
|
||||||
|
const showForm = ref(false)
|
||||||
|
const currentTransaction = ref<{ [key: string]: any } | null>(null)
|
||||||
|
const show = defineModel('show')
|
||||||
|
const billingName = ref(null)
|
||||||
|
const paymentReceived = ref(false)
|
||||||
|
const paymentForCertificate = ref(false)
|
||||||
|
const member = ref(null)
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const transactions = createListResource({
|
||||||
|
doctype: 'LMS Payment',
|
||||||
|
fields: [
|
||||||
|
'name',
|
||||||
|
'member',
|
||||||
|
'billing_name',
|
||||||
|
'source',
|
||||||
|
'payment_for_document_type',
|
||||||
|
'payment_for_document',
|
||||||
|
'payment_received',
|
||||||
|
'payment_for_certificate',
|
||||||
|
'currency',
|
||||||
|
'amount',
|
||||||
|
'order_id',
|
||||||
|
'payment_id',
|
||||||
|
'gstin',
|
||||||
|
'pan',
|
||||||
|
'address',
|
||||||
|
],
|
||||||
|
auto: true,
|
||||||
|
orderBy: 'modified desc',
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
[billingName, member, paymentReceived, paymentForCertificate],
|
||||||
|
([
|
||||||
|
newBillingName,
|
||||||
|
newMember,
|
||||||
|
newPaymentReceived,
|
||||||
|
newPaymentForCertificate,
|
||||||
|
]) => {
|
||||||
|
transactions.update({
|
||||||
|
filters: [
|
||||||
|
newBillingName ? [['billing_name', 'like', `%${newBillingName}%`]] : [],
|
||||||
|
newMember ? [['member', '=', newMember]] : [],
|
||||||
|
newPaymentReceived
|
||||||
|
? [['payment_received', '=', newPaymentReceived]]
|
||||||
|
: [],
|
||||||
|
newPaymentForCertificate
|
||||||
|
? [['payment_for_certificate', '=', newPaymentForCertificate]]
|
||||||
|
: [],
|
||||||
|
].flat(),
|
||||||
|
})
|
||||||
|
transactions.reload()
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
const openForm = (transaction: { [key: string]: any }) => {
|
||||||
|
currentTransaction.value = transaction
|
||||||
|
showForm.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCurrencySymbol = (currency: string) => {
|
||||||
|
const currencySymbols: Record<string, string> = {
|
||||||
|
USD: '$',
|
||||||
|
EUR: '€',
|
||||||
|
GBP: '£',
|
||||||
|
INR: '₹',
|
||||||
|
AED: 'د.إ',
|
||||||
|
CHF: 'Fr',
|
||||||
|
JPY: '¥',
|
||||||
|
AUD: '$',
|
||||||
|
}
|
||||||
|
return currencySymbols[currency] || currency
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = computed(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: __('Billing Name'),
|
||||||
|
icon: 'user',
|
||||||
|
key: 'billing_name',
|
||||||
|
width: '30%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __('Amount'),
|
||||||
|
icon: 'dollar-sign',
|
||||||
|
key: 'amount',
|
||||||
|
width: '20%',
|
||||||
|
align: 'right',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __('Payment Received'),
|
||||||
|
icon: 'check-circle',
|
||||||
|
key: 'payment_received',
|
||||||
|
width: '25%',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __('Payment for Certificate'),
|
||||||
|
icon: 'award',
|
||||||
|
key: 'payment_for_certificate',
|
||||||
|
width: '25%',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
</script>
|
||||||
3182
frontend/yarn.lock
3182
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ import re
|
|||||||
import shutil
|
import shutil
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
import zipfile
|
import zipfile
|
||||||
|
from dataclasses import fields
|
||||||
from xml.dom.minidom import parseString
|
from xml.dom.minidom import parseString
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
@@ -823,7 +824,6 @@ def get_count(doctype, filters):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_payment_gateway_details(payment_gateway):
|
def get_payment_gateway_details(payment_gateway):
|
||||||
fields = []
|
|
||||||
gateway = frappe.get_doc("Payment Gateway", payment_gateway)
|
gateway = frappe.get_doc("Payment Gateway", payment_gateway)
|
||||||
|
|
||||||
if gateway.gateway_controller is None:
|
if gateway.gateway_controller is None:
|
||||||
@@ -843,15 +843,30 @@ def get_payment_gateway_details(payment_gateway):
|
|||||||
except Exception:
|
except Exception:
|
||||||
frappe.throw(_("{0} Settings not found").format(payment_gateway))
|
frappe.throw(_("{0} Settings not found").format(payment_gateway))
|
||||||
|
|
||||||
|
gateway_fields = get_transformed_fields(meta, data)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"fields": gateway_fields,
|
||||||
|
"data": data,
|
||||||
|
"doctype": doctype,
|
||||||
|
"docname": docname,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_transformed_fields(meta, data=None):
|
||||||
|
transformed_fields = []
|
||||||
for row in meta:
|
for row in meta:
|
||||||
if row.fieldtype not in ["Column Break", "Section Break"]:
|
if row.fieldtype not in ["Column Break", "Section Break"]:
|
||||||
if row.fieldtype in ["Attach", "Attach Image"]:
|
if row.fieldtype in ["Attach", "Attach Image"]:
|
||||||
fieldtype = "Upload"
|
fieldtype = "Upload"
|
||||||
data[row.fieldname] = get_file_info(data.get(row.fieldname))
|
if data and data.get(row.fieldname):
|
||||||
|
data[row.fieldname] = get_file_info(data.get(row.fieldname))
|
||||||
|
elif row.fieldtype == "Check":
|
||||||
|
fieldtype = "checkbox"
|
||||||
else:
|
else:
|
||||||
fieldtype = row.fieldtype
|
fieldtype = row.fieldtype
|
||||||
|
|
||||||
fields.append(
|
transformed_fields.append(
|
||||||
{
|
{
|
||||||
"label": row.label,
|
"label": row.label,
|
||||||
"name": row.fieldname,
|
"name": row.fieldname,
|
||||||
@@ -859,12 +874,19 @@ def get_payment_gateway_details(payment_gateway):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return transformed_fields
|
||||||
"fields": fields,
|
|
||||||
"data": data,
|
|
||||||
"doctype": doctype,
|
@frappe.whitelist()
|
||||||
"docname": docname,
|
def get_new_gateway_fields(doctype):
|
||||||
}
|
try:
|
||||||
|
meta = frappe.get_meta(doctype).fields
|
||||||
|
except Exception:
|
||||||
|
frappe.throw(_("{0} not found").format(doctype))
|
||||||
|
|
||||||
|
transformed_fields = get_transformed_fields(meta)
|
||||||
|
|
||||||
|
return transformed_fields
|
||||||
|
|
||||||
|
|
||||||
def update_course_statistics():
|
def update_course_statistics():
|
||||||
|
|||||||
@@ -147,6 +147,7 @@
|
|||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "payment_for_certificate",
|
"fieldname": "payment_for_certificate",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
|
"in_standard_filter": 1,
|
||||||
"label": "Payment for Certificate"
|
"label": "Payment for Certificate"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -161,7 +162,7 @@
|
|||||||
"link_fieldname": "payment"
|
"link_fieldname": "payment"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2025-08-19 10:33:15.457678",
|
"modified": "2025-09-23 11:04:00.462274",
|
||||||
"modified_by": "sayali@frappe.io",
|
"modified_by": "sayali@frappe.io",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Payment",
|
"name": "LMS Payment",
|
||||||
|
|||||||
@@ -27,8 +27,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cypress": "^14.5.2",
|
"cypress": "^14.5.2",
|
||||||
"cypress-file-upload": "^5.0.8",
|
"cypress-file-upload": "^5.0.8",
|
||||||
"cypress-real-events": "^1.14.0",
|
"cypress-real-events": "^1.14.0"
|
||||||
"vite-plugin-pwa": "^1.0.2"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pre-commit": "^1.2.2"
|
"pre-commit": "^1.2.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user