crm/frontend/src/data/document.js
Shariq Ansari 0e177818cc fix: handle errors when creating document resources
(cherry picked from commit ca5d82f5beb9395a88bbc576cc58911360b62b03)
2025-07-30 12:44:40 +00:00

297 lines
7.7 KiB
JavaScript

import { getScript } from '@/data/script'
import { globalStore } from '@/stores/global'
import { showSettings, activeSettingsPage } from '@/composables/settings'
import { runSequentially, parseAssignees } from '@/utils'
import { createDocumentResource, createResource, toast } from 'frappe-ui'
import { ref, reactive } from 'vue'
const documentsCache = {}
const controllersCache = {}
const assigneesCache = {}
export function useDocument(doctype, docname) {
const { setupScript, scripts } = getScript(doctype)
documentsCache[doctype] = documentsCache[doctype] || {}
const error = ref('')
if (!documentsCache[doctype][docname || '']) {
if (docname) {
documentsCache[doctype][docname] = createDocumentResource({
doctype: doctype,
name: docname,
onSuccess: async () => await setupFormScript(),
onError: (err) => {
error.value = err
if (err.exc_type === 'DoesNotExistError') {
toast.error(__(err.messages[0] || 'Document does not exist'))
}
if (err.exc_type === 'PermissionError') {
toast.error(
__(
err.messages[0] ||
'You do not have permission to access this document',
),
)
}
},
setValue: {
onSuccess: () => {
triggerOnSave()
toast.success(__('Document updated successfully'))
},
onError: (err) => {
triggerOnError(err)
if (err.exc_type == 'MandatoryError') {
const fieldName = err.messages
.map((msg) => {
let arr = msg.split(': ')
return arr[arr.length - 1].trim()
})
.join(', ')
toast.error(__('Mandatory field error: {0}', [fieldName]))
return
}
err.messages?.forEach((msg) => {
toast.error(msg)
})
if (err.messages?.length === 0) {
toast.error(__('An error occurred while updating the document'))
}
console.error(err)
},
},
})
} else {
documentsCache[doctype][''] = reactive({
doc: {},
})
setupFormScript()
}
}
assigneesCache[doctype] = assigneesCache[doctype] || {}
if (!assigneesCache[doctype][docname || '']) {
assigneesCache[doctype][docname || ''] = createResource({
url: 'crm.api.doc.get_assigned_users',
cache: `assignees:${doctype}:${docname}`,
auto: docname ? true : false,
params: {
doctype: doctype,
name: docname,
},
transform: (data) => parseAssignees(data),
})
}
async function setupFormScript() {
if (
controllersCache[doctype] &&
typeof controllersCache[doctype][docname || ''] === 'object'
) {
return
}
if (!controllersCache[doctype]) {
controllersCache[doctype] = {}
}
controllersCache[doctype][docname || ''] = {}
const { makeCall } = globalStore()
let helpers = {}
helpers.crm = {
makePhoneCall: makeCall,
openSettings: (page) => {
showSettings.value = true
activeSettingsPage.value = page
},
}
const controllersArray = await setupScript(
documentsCache[doctype][docname || ''],
helpers,
)
if (!controllersArray || controllersArray.length === 0) return
const organizedControllers = {}
for (const controller of controllersArray) {
const controllerKey = controller.constructor.name // e.g., "CRMLead", "CRMProducts"
if (!organizedControllers[controllerKey]) {
organizedControllers[controllerKey] = []
}
organizedControllers[controllerKey].push(controller)
}
controllersCache[doctype][docname || ''] = organizedControllers
triggerOnLoad()
}
function getControllers(row = null) {
const _doctype = row?.doctype || doctype
const controllerKey = _doctype.replace(/\s+/g, '')
const docControllers = controllersCache[doctype]?.[docname || '']
if (
typeof docControllers === 'object' &&
docControllers !== null &&
!Array.isArray(docControllers)
) {
return docControllers[controllerKey] || []
}
return []
}
async function triggerOnLoad() {
const handler = async function () {
await (this.onLoad?.() || this.on_load?.() || this.onload?.())
}
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() {
const handler = async function () {
await (this.onSave?.() || this.on_save?.())
}
await trigger(handler)
}
async function triggerOnError() {
const handler = async function () {
await (this.onError?.() || this.on_error?.())
}
await trigger(handler)
}
async function triggerOnRefresh() {
const handler = async function () {
await this.refresh?.()
}
await trigger(handler)
}
async function triggerOnChange(fieldname, value, row) {
let oldValue = null
if (row) {
oldValue = row[fieldname]
row[fieldname] = value
} else {
oldValue = documentsCache[doctype][docname || ''].doc[fieldname]
documentsCache[doctype][docname || ''].doc[fieldname] = value
}
const handler = async function () {
this.value = value
this.oldValue = oldValue
if (row) {
this.currentRowIdx = row.idx
}
await this[fieldname]?.()
}
try {
await trigger(handler, row)
} catch (error) {
if (row) {
row[fieldname] = oldValue
} else {
documentsCache[doctype][docname || ''].doc[fieldname] = oldValue
}
console.error(handler)
throw error
}
}
async function triggerOnRowAdd(row) {
const handler = async function () {
this.currentRowIdx = row.idx
this.value = row
await this[row.parentfield + '_add']?.()
}
await trigger(handler, row)
}
async function triggerOnRowRemove(selectedRows, rows) {
const handler = async function () {
if (selectedRows.size === 1) {
const selectedRow = Array.from(selectedRows)[0]
this.currentRowIdx = rows.find((r) => r.name === selectedRow).idx
} else {
delete this.currentRowIdx
}
this.selectedRows = Array.from(selectedRows)
this.rows = rows
await this[rows[0].parentfield + '_remove']?.()
}
await trigger(handler, rows[0])
}
async function triggerOnCreateLead() {
const args = Array.from(arguments)
const handler = async function () {
await (this.onCreateLead?.(...args) || this.on_create_lead?.(...args))
}
await trigger(handler)
}
async function triggerConvertToDeal() {
const args = Array.from(arguments)
const handler = async function () {
await (this.convertToDeal?.(...args) || this.convert_to_deal?.(...args))
}
await trigger(handler)
}
async function trigger(taskFn, row = null) {
const controllers = getControllers(row)
if (!controllers.length) return
const tasks = controllers.map(
(controller) => async () => await taskFn.call(controller),
)
await runSequentially(tasks)
}
return {
document: documentsCache[doctype][docname || ''],
assignees: assigneesCache[doctype][docname || ''],
scripts,
error,
getControllers,
triggerOnLoad,
triggerOnBeforeCreate,
triggerOnSave,
triggerOnError,
triggerOnRefresh,
triggerOnChange,
triggerOnRowAdd,
triggerOnRowRemove,
setupFormScript,
triggerOnCreateLead,
triggerConvertToDeal,
}
}