Compare commits
No commits in common. "main" and "mergify/bp/main-hotfix/pr-1262" have entirely different histories.
main
...
mergify/bp
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,5 +7,6 @@ dev-dist
|
||||
tags
|
||||
node_modules
|
||||
crm/public/frontend
|
||||
frontend/yarn.lock
|
||||
crm/www/crm.html
|
||||
build
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
|
||||
__version__ = "1.53.1"
|
||||
__version__ = "1.52.10"
|
||||
__title__ = "Frappe CRM"
|
||||
|
||||
|
||||
137
crm/api/doc.py
137
crm/api/doc.py
@ -750,11 +750,7 @@ def getCounts(d, doctype):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_linked_docs_of_document(doctype, docname):
|
||||
try:
|
||||
doc = frappe.get_doc(doctype, docname)
|
||||
except frappe.DoesNotExistError:
|
||||
return []
|
||||
|
||||
doc = frappe.get_doc(doctype, docname)
|
||||
linked_docs = get_linked_docs(doc)
|
||||
dynamic_linked_docs = get_dynamic_linked_docs(doc)
|
||||
|
||||
@ -763,14 +759,7 @@ def get_linked_docs_of_document(doctype, docname):
|
||||
|
||||
docs_data = []
|
||||
for doc in linked_docs:
|
||||
if not doc.get("reference_doctype") or not doc.get("reference_docname"):
|
||||
continue
|
||||
|
||||
try:
|
||||
data = frappe.get_doc(doc["reference_doctype"], doc["reference_docname"])
|
||||
except (frappe.DoesNotExistError, frappe.ValidationError):
|
||||
continue
|
||||
|
||||
data = frappe.get_doc(doc["reference_doctype"], doc["reference_docname"])
|
||||
title = data.get("title")
|
||||
if data.doctype == "CRM Call Log":
|
||||
title = f"Call from {data.get('from')} to {data.get('to')}"
|
||||
@ -778,9 +767,6 @@ def get_linked_docs_of_document(doctype, docname):
|
||||
if data.doctype == "CRM Deal":
|
||||
title = data.get("organization")
|
||||
|
||||
if data.doctype == "CRM Notification":
|
||||
title = data.get("message")
|
||||
|
||||
docs_data.append(
|
||||
{
|
||||
"doc": data.doctype,
|
||||
@ -793,51 +779,25 @@ def get_linked_docs_of_document(doctype, docname):
|
||||
|
||||
|
||||
def remove_doc_link(doctype, docname):
|
||||
if not doctype or not docname:
|
||||
return
|
||||
|
||||
try:
|
||||
linked_doc_data = frappe.get_doc(doctype, docname)
|
||||
if doctype == "CRM Notification":
|
||||
delete_notification_type = {
|
||||
"notification_type_doctype": "",
|
||||
"notification_type_doc": "",
|
||||
}
|
||||
delete_references = {
|
||||
"reference_doctype": "",
|
||||
"reference_name": "",
|
||||
}
|
||||
if linked_doc_data.get("notification_type_doctype") == linked_doc_data.get("reference_doctype"):
|
||||
delete_references.update(delete_notification_type)
|
||||
|
||||
linked_doc_data.update(delete_references)
|
||||
else:
|
||||
linked_doc_data.update(
|
||||
{
|
||||
"reference_doctype": "",
|
||||
"reference_docname": "",
|
||||
}
|
||||
)
|
||||
linked_doc_data.save(ignore_permissions=True)
|
||||
except (frappe.DoesNotExistError, frappe.ValidationError):
|
||||
pass
|
||||
linked_doc_data = frappe.get_doc(doctype, docname)
|
||||
linked_doc_data.update(
|
||||
{
|
||||
"reference_doctype": None,
|
||||
"reference_docname": None,
|
||||
}
|
||||
)
|
||||
linked_doc_data.save(ignore_permissions=True)
|
||||
|
||||
|
||||
def remove_contact_link(doctype, docname):
|
||||
if not doctype or not docname:
|
||||
return
|
||||
|
||||
try:
|
||||
linked_doc_data = frappe.get_doc(doctype, docname)
|
||||
linked_doc_data.update(
|
||||
{
|
||||
"contact": None,
|
||||
"contacts": [],
|
||||
}
|
||||
)
|
||||
linked_doc_data.save(ignore_permissions=True)
|
||||
except (frappe.DoesNotExistError, frappe.ValidationError):
|
||||
pass
|
||||
linked_doc_data = frappe.get_doc(doctype, docname)
|
||||
linked_doc_data.update(
|
||||
{
|
||||
"contact": None,
|
||||
"contacts": [],
|
||||
}
|
||||
)
|
||||
linked_doc_data.save(ignore_permissions=True)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@ -846,19 +806,13 @@ def remove_linked_doc_reference(items, remove_contact=None, delete=False):
|
||||
items = frappe.parse_json(items)
|
||||
|
||||
for item in items:
|
||||
if not item.get("doctype") or not item.get("docname"):
|
||||
continue
|
||||
if remove_contact:
|
||||
remove_contact_link(item["doctype"], item["docname"])
|
||||
else:
|
||||
remove_doc_link(item["doctype"], item["docname"])
|
||||
|
||||
try:
|
||||
if remove_contact:
|
||||
remove_contact_link(item["doctype"], item["docname"])
|
||||
else:
|
||||
remove_doc_link(item["doctype"], item["docname"])
|
||||
if delete:
|
||||
frappe.delete_doc(item["doctype"], item["docname"])
|
||||
except (frappe.DoesNotExistError, frappe.ValidationError):
|
||||
# Skip if document doesn't exist or has validation errors
|
||||
continue
|
||||
if delete:
|
||||
frappe.delete_doc(item["doctype"], item["docname"])
|
||||
|
||||
return "success"
|
||||
|
||||
@ -867,40 +821,19 @@ def remove_linked_doc_reference(items, remove_contact=None, delete=False):
|
||||
def delete_bulk_docs(doctype, items, delete_linked=False):
|
||||
from frappe.desk.reportview import delete_bulk
|
||||
|
||||
if not doctype:
|
||||
frappe.throw("Doctype is required")
|
||||
|
||||
if not items:
|
||||
frappe.throw("Items are required")
|
||||
|
||||
items = frappe.parse_json(items)
|
||||
if not isinstance(items, list):
|
||||
frappe.throw("Items must be a list")
|
||||
|
||||
for doc in items:
|
||||
try:
|
||||
if not frappe.db.exists(doctype, doc):
|
||||
frappe.log_error(f"Document {doctype} {doc} does not exist", "Bulk Delete Error")
|
||||
continue
|
||||
|
||||
linked_docs = get_linked_docs_of_document(doctype, doc)
|
||||
for linked_doc in linked_docs:
|
||||
if not linked_doc.get("reference_doctype") or not linked_doc.get("reference_docname"):
|
||||
continue
|
||||
|
||||
remove_linked_doc_reference(
|
||||
[
|
||||
{
|
||||
"doctype": linked_doc["reference_doctype"],
|
||||
"docname": linked_doc["reference_docname"],
|
||||
}
|
||||
],
|
||||
remove_contact=doctype == "Contact",
|
||||
delete=delete_linked,
|
||||
)
|
||||
except Exception as e:
|
||||
frappe.log_error(
|
||||
f"Error processing linked docs for {doctype} {doc}: {str(e)}", "Bulk Delete Error"
|
||||
linked_docs = get_linked_docs_of_document(doctype, doc)
|
||||
for linked_doc in linked_docs:
|
||||
remove_linked_doc_reference(
|
||||
[
|
||||
{
|
||||
"doctype": linked_doc["reference_doctype"],
|
||||
"docname": linked_doc["reference_docname"],
|
||||
}
|
||||
],
|
||||
remove_contact=doctype == "Contact",
|
||||
delete=delete_linked,
|
||||
)
|
||||
|
||||
if len(items) > 10:
|
||||
|
||||
543
crm/locale/ar.po
543
crm/locale/ar.po
File diff suppressed because it is too large
Load Diff
549
crm/locale/bs.po
549
crm/locale/bs.po
File diff suppressed because it is too large
Load Diff
543
crm/locale/cs.po
543
crm/locale/cs.po
File diff suppressed because it is too large
Load Diff
543
crm/locale/da.po
543
crm/locale/da.po
File diff suppressed because it is too large
Load Diff
543
crm/locale/de.po
543
crm/locale/de.po
File diff suppressed because it is too large
Load Diff
549
crm/locale/eo.po
549
crm/locale/eo.po
File diff suppressed because it is too large
Load Diff
543
crm/locale/es.po
543
crm/locale/es.po
File diff suppressed because it is too large
Load Diff
543
crm/locale/fa.po
543
crm/locale/fa.po
File diff suppressed because it is too large
Load Diff
543
crm/locale/fr.po
543
crm/locale/fr.po
File diff suppressed because it is too large
Load Diff
549
crm/locale/hr.po
549
crm/locale/hr.po
File diff suppressed because it is too large
Load Diff
543
crm/locale/hu.po
543
crm/locale/hu.po
File diff suppressed because it is too large
Load Diff
545
crm/locale/id.po
545
crm/locale/id.po
File diff suppressed because it is too large
Load Diff
543
crm/locale/it.po
543
crm/locale/it.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
577
crm/locale/nb.po
577
crm/locale/nb.po
File diff suppressed because it is too large
Load Diff
543
crm/locale/nl.po
543
crm/locale/nl.po
File diff suppressed because it is too large
Load Diff
543
crm/locale/pl.po
543
crm/locale/pl.po
File diff suppressed because it is too large
Load Diff
543
crm/locale/pt.po
543
crm/locale/pt.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
543
crm/locale/ru.po
543
crm/locale/ru.po
File diff suppressed because it is too large
Load Diff
549
crm/locale/sr.po
549
crm/locale/sr.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
549
crm/locale/sv.po
549
crm/locale/sv.po
File diff suppressed because it is too large
Load Diff
543
crm/locale/th.po
543
crm/locale/th.po
File diff suppressed because it is too large
Load Diff
543
crm/locale/tr.po
543
crm/locale/tr.po
File diff suppressed because it is too large
Load Diff
543
crm/locale/vi.po
543
crm/locale/vi.po
File diff suppressed because it is too large
Load Diff
543
crm/locale/zh.po
543
crm/locale/zh.po
File diff suppressed because it is too large
Load Diff
@ -1 +1 @@
|
||||
Subproject commit c9a0fc937cc897864857271b3708a0c675379015
|
||||
Subproject commit 02fc126fd5c49f0ecf6cce117585f89c4ea585c3
|
||||
@ -13,7 +13,7 @@
|
||||
"@tiptap/extension-paragraph": "^2.12.0",
|
||||
"@twilio/voice-sdk": "^2.10.2",
|
||||
"@vueuse/integrations": "^10.3.0",
|
||||
"frappe-ui": "^0.1.201",
|
||||
"frappe-ui": "^0.1.197",
|
||||
"gemoji": "^8.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mime": "^4.0.1",
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-ink-gray-5 text-base">
|
||||
<div class="text-ink-gray-5">
|
||||
{{
|
||||
__('Are you sure you want to delete {0} items?', [
|
||||
props.items?.length,
|
||||
@ -53,7 +53,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-ink-gray-5 text-base">
|
||||
<div class="text-ink-gray-5">
|
||||
{{
|
||||
confirmDeleteInfo.delete
|
||||
? __(
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<Dialog v-model="show" :options="{ size: 'xl' }">
|
||||
<template #body v-if="!confirmDeleteInfo.show">
|
||||
<div class="bg-surface-modal px-4 pb-6 pt-5 sm:px-6">
|
||||
<div class="mb-6 flex items-center justify-between">
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-2xl leading-6 text-ink-gray-9 font-semibold">
|
||||
{{
|
||||
@ -32,12 +32,11 @@
|
||||
{
|
||||
label: 'Document',
|
||||
key: 'title',
|
||||
width: '19rem',
|
||||
},
|
||||
{
|
||||
label: 'Master',
|
||||
key: 'reference_doctype',
|
||||
width: '12rem',
|
||||
width: '30%',
|
||||
},
|
||||
]"
|
||||
@selectionsChanged="
|
||||
|
||||
@ -26,14 +26,13 @@
|
||||
<ListRowItem
|
||||
:item="item"
|
||||
@click="listViewRef.toggleRow(row['reference_docname'])"
|
||||
class="!w-full"
|
||||
>
|
||||
<template #default="{ label }">
|
||||
<div
|
||||
v-if="column.key === 'title'"
|
||||
class="truncate text-base flex gap-2 w-full"
|
||||
class="truncate text-base flex gap-2"
|
||||
>
|
||||
<span class="max-w-[90%] truncate">
|
||||
<span>
|
||||
{{ label }}
|
||||
</span>
|
||||
<FeatherIcon
|
||||
@ -103,7 +102,6 @@ const listViewRef = ref(null)
|
||||
const viewLinkedDoc = (doc) => {
|
||||
let page = ''
|
||||
let id = ''
|
||||
let openDesk = false
|
||||
switch (doc.reference_doctype) {
|
||||
case 'CRM Lead':
|
||||
page = 'leads'
|
||||
@ -125,11 +123,6 @@ const viewLinkedDoc = (doc) => {
|
||||
page = 'organizations'
|
||||
id = doc.reference_docname
|
||||
break
|
||||
case 'CRM Notification':
|
||||
page = 'crm-notification'
|
||||
id = doc.reference_docname
|
||||
openDesk = true
|
||||
break
|
||||
case 'FCRM Note':
|
||||
page = 'notes'
|
||||
id = `view?open=${doc.reference_docname}`
|
||||
@ -137,11 +130,7 @@ const viewLinkedDoc = (doc) => {
|
||||
default:
|
||||
break
|
||||
}
|
||||
let base = '/crm'
|
||||
if (openDesk) {
|
||||
base = '/app'
|
||||
}
|
||||
window.open(`${base}/${page}/${id}`)
|
||||
window.open(`/crm/${page}/${id}`)
|
||||
}
|
||||
|
||||
const getDoctypeName = (doctype) => {
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { getScript } from '@/data/script'
|
||||
import { globalStore } from '@/stores/global'
|
||||
import { getMeta } from '@/stores/meta'
|
||||
import { showSettings, activeSettingsPage } from '@/composables/settings'
|
||||
import { runSequentially, parseAssignees, evaluateExpression } from '@/utils'
|
||||
import { runSequentially, parseAssignees } from '@/utils'
|
||||
import { createDocumentResource, createResource, toast } from 'frappe-ui'
|
||||
import { ref, reactive } from 'vue'
|
||||
|
||||
@ -12,7 +11,6 @@ const assigneesCache = {}
|
||||
|
||||
export function useDocument(doctype, docname) {
|
||||
const { setupScript, scripts } = getScript(doctype)
|
||||
const meta = getMeta(doctype)
|
||||
|
||||
documentsCache[doctype] = documentsCache[doctype] || {}
|
||||
|
||||
@ -39,7 +37,6 @@ export function useDocument(doctype, docname) {
|
||||
}
|
||||
},
|
||||
setValue: {
|
||||
validate,
|
||||
onSuccess: () => {
|
||||
triggerOnSave()
|
||||
toast.success(__('Document updated successfully'))
|
||||
@ -155,42 +152,6 @@ export function useDocument(doctype, docname) {
|
||||
return []
|
||||
}
|
||||
|
||||
function validate(d) {
|
||||
checkMandatory(d.doc || d.fieldname)
|
||||
}
|
||||
|
||||
function checkMandatory(doc) {
|
||||
let fields = meta?.getFields() || []
|
||||
|
||||
if (!fields || fields.length === 0) return
|
||||
|
||||
let missingFields = []
|
||||
|
||||
fields.forEach((df) => {
|
||||
let parent = meta?.doctypeMeta?.[df.parent] || null
|
||||
if (evaluateExpression(df.mandatory_depends_on, doc, parent)) {
|
||||
const value = doc[df.fieldname]
|
||||
if (
|
||||
value === undefined ||
|
||||
value === null ||
|
||||
(typeof value === 'string' && value.trim() === '') ||
|
||||
(Array.isArray(value) && value.length === 0)
|
||||
) {
|
||||
missingFields.push(df.label || df.fieldname)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (missingFields.length > 0) {
|
||||
toast.error(
|
||||
__('Mandatory fields required: {0}', [missingFields.join(', ')]),
|
||||
)
|
||||
throw new Error(
|
||||
__('Mandatory fields required: {0}', [missingFields.join(', ')]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function triggerOnLoad() {
|
||||
const handler = async function () {
|
||||
await (this.onLoad?.() || this.on_load?.() || this.onload?.())
|
||||
@ -319,7 +280,6 @@ export function useDocument(doctype, docname) {
|
||||
assignees: assigneesCache[doctype][docname || ''],
|
||||
scripts,
|
||||
error,
|
||||
validate,
|
||||
getControllers,
|
||||
triggerOnLoad,
|
||||
triggerOnBeforeCreate,
|
||||
|
||||
@ -421,36 +421,6 @@ export function evaluateDependsOnValue(expression, doc) {
|
||||
return out
|
||||
}
|
||||
|
||||
export function evaluateExpression(expression, doc, parent) {
|
||||
if (!expression) return false
|
||||
if (!doc) return false
|
||||
|
||||
let out = null
|
||||
if (typeof expression === 'boolean') {
|
||||
out = expression
|
||||
} else if (typeof expression === 'function') {
|
||||
out = expression(doc)
|
||||
} else if (expression.substr(0, 5) == 'eval:') {
|
||||
try {
|
||||
out = _eval(expression.substr(5), { doc, parent })
|
||||
if (parent && parent.istable && expression.includes('is_submittable')) {
|
||||
out = true
|
||||
}
|
||||
} catch (e) {
|
||||
out = true
|
||||
}
|
||||
} else {
|
||||
let value = doc[expression]
|
||||
if (Array.isArray(value)) {
|
||||
out = !!value.length
|
||||
} else {
|
||||
out = !!value
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
export function convertSize(size) {
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
let unitIndex = 0
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user