Merge pull request #985 from shariquerik/on-before-create

This commit is contained in:
Shariq Ansari 2025-06-30 19:41:35 +05:30 committed by GitHub
commit 2014a3d6de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 133 additions and 106 deletions

View File

@ -84,7 +84,10 @@ const error = ref(null)
const title = ref(null) const title = ref(null)
const editMode = ref(false) const editMode = ref(false)
const { document: _address } = useDocument('Address', props.address || '') const { document: _address, triggerOnBeforeCreate } = useDocument(
'Address',
props.address || '',
)
const dialogOptions = computed(() => { const dialogOptions = computed(() => {
let title = !editMode.value let title = !editMode.value
@ -95,8 +98,7 @@ const dialogOptions = computed(() => {
{ {
label: editMode.value ? __('Save') : __('Create'), label: editMode.value ? __('Save') : __('Create'),
variant: 'solid', variant: 'solid',
onClick: () => onClick: () => (editMode.value ? updateAddress() : createAddress()),
editMode.value ? updateAddress() : createAddress.submit(),
}, },
] ]
@ -133,16 +135,22 @@ async function updateAddress() {
await _address.save.submit(null, callBacks) await _address.save.submit(null, callBacks)
} }
const createAddress = createResource({ async function createAddress() {
loading.value = true
error.value = null
await triggerOnBeforeCreate?.()
await _createAddress.submit({
doc: {
doctype: 'Address',
..._address.doc,
},
})
}
const _createAddress = createResource({
url: 'frappe.client.insert', url: 'frappe.client.insert',
makeParams() {
return {
doc: {
doctype: 'Address',
..._address.doc,
},
}
},
onSuccess(doc) { onSuccess(doc) {
loading.value = false loading.value = false
if (doc.name) { if (doc.name) {

View File

@ -85,7 +85,7 @@ const loading = ref(false)
const error = ref(null) const error = ref(null)
const editMode = ref(false) const editMode = ref(false)
const { document: callLog } = useDocument( const { document: callLog, triggerOnBeforeCreate } = useDocument(
'CRM Call Log', 'CRM Call Log',
props.data?.name || '', props.data?.name || '',
) )
@ -97,8 +97,7 @@ const dialogOptions = computed(() => {
{ {
label: editMode.value ? __('Save') : __('Create'), label: editMode.value ? __('Save') : __('Create'),
variant: 'solid', variant: 'solid',
onClick: () => onClick: () => (editMode.value ? updateCallLog() : createCallLog()),
editMode.value ? updateCallLog() : createCallLog.submit(),
}, },
] ]
@ -135,18 +134,21 @@ async function updateCallLog() {
await callLog.save.submit(null, callBacks) await callLog.save.submit(null, callBacks)
} }
const createCallLog = createResource({ async function createCallLog() {
Object.assign(callLog.doc, {
doctype: 'CRM Call Log',
id: getRandom(6),
telephony_medium: 'Manual',
})
await triggerOnBeforeCreate?.()
await _createCallLog.submit({
doc: callLog.doc,
})
}
const _createCallLog = createResource({
url: 'frappe.client.insert', url: 'frappe.client.insert',
makeParams() {
return {
doc: {
doctype: 'CRM Call Log',
id: getRandom(6),
telephony_medium: 'Manual',
...callLog.doc,
},
}
},
onSuccess(doc) { onSuccess(doc) {
loading.value = false loading.value = false
if (doc.name) { if (doc.name) {

View File

@ -86,7 +86,7 @@ const show = defineModel()
const loading = ref(false) const loading = ref(false)
const { document: _contact } = useDocument('Contact') const { document: _contact, triggerOnBeforeCreate } = useDocument('Contact')
async function createContact() { async function createContact() {
if (_contact.doc.email_id) { if (_contact.doc.email_id) {
@ -99,6 +99,8 @@ async function createContact() {
delete _contact.doc.mobile_no delete _contact.doc.mobile_no
} }
await triggerOnBeforeCreate?.()
const doc = await call('frappe.client.insert', { const doc = await call('frappe.client.insert', {
doc: { doc: {
doctype: 'Contact', doctype: 'Contact',

View File

@ -27,7 +27,7 @@
</div> </div>
</div> </div>
<div v-if="tabs.data"> <div v-if="tabs.data">
<FieldLayout :tabs="tabs.data" :data="_data" :doctype="doctype" /> <FieldLayout :tabs="tabs.data" :data="_data.doc" :doctype="doctype" />
<ErrorMessage class="mt-2" :message="error" /> <ErrorMessage class="mt-2" :message="error" />
</div> </div>
</div> </div>
@ -51,6 +51,7 @@
import FieldLayout from '@/components/FieldLayout/FieldLayout.vue' import FieldLayout from '@/components/FieldLayout/FieldLayout.vue'
import EditIcon from '@/components/Icons/EditIcon.vue' import EditIcon from '@/components/Icons/EditIcon.vue'
import { usersStore } from '@/stores/users' import { usersStore } from '@/stores/users'
import { useDocument } from '@/data/document'
import { isMobileView } from '@/composables/settings' import { isMobileView } from '@/composables/settings'
import { showQuickEntryModal, quickEntryProps } from '@/composables/modals' import { showQuickEntryModal, quickEntryProps } from '@/composables/modals'
import { FeatherIcon, createResource, ErrorMessage, call } from 'frappe-ui' import { FeatherIcon, createResource, ErrorMessage, call } from 'frappe-ui'
@ -76,7 +77,7 @@ const show = defineModel()
const loading = ref(false) const loading = ref(false)
const error = ref(null) const error = ref(null)
let _data = ref({}) const { document: _data, triggerOnBeforeCreate } = useDocument(props.doctype)
const dialogOptions = computed(() => { const dialogOptions = computed(() => {
let doctype = props.doctype let doctype = props.doctype
@ -109,12 +110,14 @@ async function create() {
loading.value = true loading.value = true
error.value = null error.value = null
await triggerOnBeforeCreate?.()
let doc = await call( let doc = await call(
'frappe.client.insert', 'frappe.client.insert',
{ {
doc: { doc: {
doctype: props.doctype, doctype: props.doctype,
..._data.value, ..._data.doc,
}, },
}, },
{ {
@ -138,7 +141,7 @@ watch(
if (!value) return if (!value) return
nextTick(() => { nextTick(() => {
_data.value = { ...props.data } _data.doc = { ...props.data }
}) })
}, },
) )

View File

@ -98,7 +98,11 @@ const show = defineModel()
const router = useRouter() const router = useRouter()
const error = ref(null) const error = ref(null)
const { document: deal, triggerOnChange } = useDocument('CRM Deal') const {
document: deal,
triggerOnChange,
triggerOnBeforeCreate,
} = useDocument('CRM Deal')
const hasOrganizationSections = ref(true) const hasOrganizationSections = ref(true)
const hasContactSections = ref(true) const hasContactSections = ref(true)
@ -175,7 +179,7 @@ const dealStatuses = computed(() => {
return statuses return statuses
}) })
function createDeal() { async function createDeal() {
if (deal.doc.website && !deal.doc.website.startsWith('http')) { if (deal.doc.website && !deal.doc.website.startsWith('http')) {
deal.doc.website = 'https://' + deal.doc.website deal.doc.website = 'https://' + deal.doc.website
} }
@ -186,6 +190,8 @@ function createDeal() {
deal.doc['mobile_no'] = null deal.doc['mobile_no'] = null
} else deal.doc['contact'] = null } else deal.doc['contact'] = null
await triggerOnBeforeCreate?.()
createResource({ createResource({
url: 'crm.fcrm.doctype.crm_deal.crm_deal.create_deal', url: 'crm.fcrm.doctype.crm_deal.crm_deal.create_deal',
params: { args: deal.doc }, params: { args: deal.doc },

View File

@ -74,7 +74,11 @@ const router = useRouter()
const error = ref(null) const error = ref(null)
const isLeadCreating = ref(false) const isLeadCreating = ref(false)
const { document: lead, triggerOnChange } = useDocument('CRM Lead') const {
document: lead,
triggerOnChange,
triggerOnBeforeCreate,
} = useDocument('CRM Lead')
const leadStatuses = computed(() => { const leadStatuses = computed(() => {
let statuses = statusOptions('lead', null, [], triggerOnChange) let statuses = statusOptions('lead', null, [], triggerOnChange)
@ -112,71 +116,73 @@ const tabs = createResource({
const createLead = createResource({ const createLead = createResource({
url: 'frappe.client.insert', url: 'frappe.client.insert',
makeParams(values) {
return {
doc: {
doctype: 'CRM Lead',
...values,
},
}
},
}) })
function createNewLead() { async function createNewLead() {
if (lead.doc.website && !lead.doc.website.startsWith('http')) { if (lead.doc.website && !lead.doc.website.startsWith('http')) {
lead.doc.website = 'https://' + lead.doc.website lead.doc.website = 'https://' + lead.doc.website
} }
createLead.submit(lead.doc, { await triggerOnBeforeCreate?.()
validate() {
error.value = null createLead.submit(
if (!lead.doc.first_name) { {
error.value = __('First Name is mandatory') doc: {
return error.value doctype: 'CRM Lead',
} ...lead.doc,
if (lead.doc.annual_revenue) { },
if (typeof lead.doc.annual_revenue === 'string') { },
lead.doc.annual_revenue = lead.doc.annual_revenue.replace(/,/g, '') {
} else if (isNaN(lead.doc.annual_revenue)) { validate() {
error.value = __('Annual Revenue should be a number') error.value = null
if (!lead.doc.first_name) {
error.value = __('First Name is mandatory')
return error.value return error.value
} }
} if (lead.doc.annual_revenue) {
if ( if (typeof lead.doc.annual_revenue === 'string') {
lead.doc.mobile_no && lead.doc.annual_revenue = lead.doc.annual_revenue.replace(/,/g, '')
isNaN(lead.doc.mobile_no.replace(/[-+() ]/g, '')) } else if (isNaN(lead.doc.annual_revenue)) {
) { error.value = __('Annual Revenue should be a number')
error.value = __('Mobile No should be a number') return error.value
return error.value }
} }
if (lead.doc.email && !lead.doc.email.includes('@')) { if (
error.value = __('Invalid Email') lead.doc.mobile_no &&
return error.value isNaN(lead.doc.mobile_no.replace(/[-+() ]/g, ''))
} ) {
if (!lead.doc.status) { error.value = __('Mobile No should be a number')
error.value = __('Status is required') return error.value
return error.value }
} if (lead.doc.email && !lead.doc.email.includes('@')) {
isLeadCreating.value = true error.value = __('Invalid Email')
return error.value
}
if (!lead.doc.status) {
error.value = __('Status is required')
return error.value
}
isLeadCreating.value = true
},
onSuccess(data) {
capture('lead_created')
isLeadCreating.value = false
show.value = false
router.push({ name: 'Lead', params: { leadId: data.name } })
updateOnboardingStep('create_first_lead', true, false, () => {
localStorage.setItem('firstLead' + user, data.name)
})
},
onError(err) {
isLeadCreating.value = false
if (!err.messages) {
error.value = err.message
return
}
error.value = err.messages.join('\n')
},
}, },
onSuccess(data) { )
capture('lead_created')
isLeadCreating.value = false
show.value = false
router.push({ name: 'Lead', params: { leadId: data.name } })
updateOnboardingStep('create_first_lead', true, false, () => {
localStorage.setItem('firstLead' + user, data.name)
})
},
onError(err) {
isLeadCreating.value = false
if (!err.messages) {
error.value = err.message
return
}
error.value = err.messages.join('\n')
},
})
} }
function openQuickEntryModal() { function openQuickEntryModal() {

View File

@ -88,9 +88,15 @@ const show = defineModel()
const loading = ref(false) const loading = ref(false)
const error = ref(null) const error = ref(null)
const { document: organization } = useDocument('CRM Organization') const { document: organization, triggerOnBeforeCreate } =
useDocument('CRM Organization')
async function createOrganization() { async function createOrganization() {
loading.value = true
error.value = null
await triggerOnBeforeCreate?.()
const doc = await call( const doc = await call(
'frappe.client.insert', 'frappe.client.insert',
{ {

View File

@ -110,6 +110,14 @@ export function useDocument(doctype, docname) {
await trigger(handler) await trigger(handler)
} }
async function triggerOnBeforeCreate() {
const args = Array.from(arguments)
const handler = async function () {
await (this.onBeforeCreate?.(...args) || this.on_before_create?.(...args))
}
await trigger(handler)
}
async function triggerOnSave() { async function triggerOnSave() {
const handler = async function () { const handler = async function () {
await (this.onSave?.() || this.on_save?.()) await (this.onSave?.() || this.on_save?.())
@ -202,26 +210,12 @@ export function useDocument(doctype, docname) {
await runSequentially(tasks) await runSequentially(tasks)
} }
function getOldValue(fieldname, row) {
if (!documentsCache[doctype][docname || '']) return ''
const document = documentsCache[doctype][docname || '']
const oldDoc = document.originalDoc
if (row?.name) {
return oldDoc?.[row.parentfield]?.find((r) => r.name === row.name)?.[
fieldname
]
}
return oldDoc?.[fieldname] || document.doc[fieldname]
}
return { return {
document: documentsCache[doctype][docname || ''], document: documentsCache[doctype][docname || ''],
assignees, assignees,
getControllers, getControllers,
triggerOnLoad, triggerOnLoad,
triggerOnBeforeCreate,
triggerOnSave, triggerOnSave,
triggerOnRefresh, triggerOnRefresh,
triggerOnChange, triggerOnChange,