Merge branch 'develop' into website_theme_hook
This commit is contained in:
commit
f06807daab
27 changed files with 485 additions and 230 deletions
|
|
@ -59,15 +59,18 @@ context('Recorder', () => {
|
|||
cy.get('.title-text').should('contain', 'DocType');
|
||||
cy.get('.list-count').should('contain', '20 of ');
|
||||
|
||||
cy.visit('/desk#recorder');
|
||||
// temporarily commenting out theses tests as they seem to be
|
||||
// randomly failing maybe due a backround event
|
||||
|
||||
cy.get('.list-row-container span').contains('/api/method/frappe').click();
|
||||
// cy.visit('/desk#recorder');
|
||||
|
||||
cy.location('hash').should('contain', '#recorder/request/');
|
||||
cy.get('form').should('contain', '/api/method/frappe');
|
||||
// cy.get('.list-row-container span').contains('/api/method/frappe').click();
|
||||
|
||||
cy.get('#page-recorder .primary-action').should('contain', 'Stop').click();
|
||||
cy.get('#page-recorder .btn-secondary').should('contain', 'Clear').click();
|
||||
cy.location('hash').should('eq', '#recorder');
|
||||
// cy.location('hash').should('contain', '#recorder/request/');
|
||||
// cy.get('form').should('contain', '/api/method/frappe');
|
||||
|
||||
// cy.get('#page-recorder .primary-action').should('contain', 'Stop').click();
|
||||
// cy.get('#page-recorder .btn-secondary').should('contain', 'Clear').click();
|
||||
// cy.location('hash').should('eq', '#recorder');
|
||||
});
|
||||
});
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:salutation",
|
||||
"beta": 0,
|
||||
"creation": "2017-04-10 12:17:58.071915",
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-04-10 12:55:18.855578",
|
||||
"modified": "2020-09-14 12:55:18.855578",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Contacts",
|
||||
"name": "Salutation",
|
||||
|
|
@ -129,4 +129,4 @@
|
|||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -636,13 +636,15 @@ class DocType(Document):
|
|||
if not name:
|
||||
name = self.name
|
||||
|
||||
flags = {"flags": re.ASCII} if six.PY3 else {}
|
||||
|
||||
# a DocType name should not start or end with an empty space
|
||||
if re.match("^[ \t\n\r]+|[ \t\n\r]+$", name, **flags):
|
||||
frappe.throw(_("DocType's name should not start or end with whitespace"), frappe.NameError)
|
||||
|
||||
# a DocType's name should not start with a number or underscore
|
||||
# and should only contain letters, numbers and underscore
|
||||
if six.PY2:
|
||||
is_a_valid_name = re.match("^(?![\W])[^\d_\s][\w ]+$", name)
|
||||
else:
|
||||
is_a_valid_name = re.match("^(?![\W])[^\d_\s][\w ]+$", name, flags = re.ASCII)
|
||||
if not is_a_valid_name:
|
||||
if not re.match("^(?![\W])[^\d_\s][\w ]+$", name, **flags):
|
||||
frappe.throw(_("DocType's name should start with a letter and it can only consist of letters, numbers, spaces and underscores"), frappe.NameError)
|
||||
|
||||
|
||||
|
|
|
|||
0
frappe/core/doctype/document_naming_rule/__init__.py
Normal file
0
frappe/core/doctype/document_naming_rule/__init__.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) 2020, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Document Naming Rule', {
|
||||
refresh: function(frm) {
|
||||
frm.trigger('document_type');
|
||||
},
|
||||
document_type: (frm) => {
|
||||
// update the select field options with fieldnames
|
||||
if (frm.doc.document_type) {
|
||||
frappe.model.with_doctype(frm.doc.document_type, () => {
|
||||
let fieldnames = frappe.get_meta(frm.doc.document_type).fields
|
||||
.filter((d) => {
|
||||
return frappe.model.no_value_type.indexOf(d.fieldtype) === -1;
|
||||
}).map((d) => {
|
||||
return {label: `${d.label} (${d.fieldname})`, value: d.fieldname};
|
||||
});
|
||||
frappe.meta.get_docfield('Document Naming Rule Condition', 'field', frm.doc.name).options = fieldnames;
|
||||
frm.refresh_field('conditions');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
{
|
||||
"actions": [],
|
||||
"creation": "2020-09-07 12:48:48.334318",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"document_type",
|
||||
"disabled",
|
||||
"priority",
|
||||
"section_break_3",
|
||||
"conditions",
|
||||
"naming_section",
|
||||
"prefix",
|
||||
"prefix_digits",
|
||||
"counter"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "document_type",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Document Type",
|
||||
"options": "DocType"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "disabled",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disabled"
|
||||
},
|
||||
{
|
||||
"fieldname": "prefix",
|
||||
"fieldtype": "Data",
|
||||
"label": "Prefix",
|
||||
"mandatory_depends_on": "eval:doc.naming_by===\"Numbered\""
|
||||
},
|
||||
{
|
||||
"fieldname": "counter",
|
||||
"fieldtype": "Int",
|
||||
"label": "Counter",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "5",
|
||||
"description": "Example: 00001",
|
||||
"fieldname": "prefix_digits",
|
||||
"fieldtype": "Int",
|
||||
"label": "Digits",
|
||||
"mandatory_depends_on": "eval:doc.naming_by===\"Numbered\""
|
||||
},
|
||||
{
|
||||
"fieldname": "naming_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Naming"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "conditions",
|
||||
"fieldname": "section_break_3",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Rule Conditions"
|
||||
},
|
||||
{
|
||||
"fieldname": "conditions",
|
||||
"fieldtype": "Table",
|
||||
"label": "Conditions",
|
||||
"options": "Document Naming Rule Condition"
|
||||
},
|
||||
{
|
||||
"description": "Rules with higher priority will be applied first.",
|
||||
"fieldname": "priority",
|
||||
"fieldtype": "Int",
|
||||
"label": "Priority"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2020-09-21 10:23:34.401539",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Document Naming Rule",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "document_type",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils.data import evaluate_filters
|
||||
|
||||
class DocumentNamingRule(Document):
|
||||
def apply(self, doc):
|
||||
'''
|
||||
Apply naming rules for the given document. Will set `name` if the rule is matched.
|
||||
'''
|
||||
if self.conditions:
|
||||
if not evaluate_filters(doc, [(d.field, d.condition, d.value) for d in self.conditions]):
|
||||
return
|
||||
|
||||
counter = frappe.db.get_value(self.doctype, self.name, 'counter', for_update=True) or 0
|
||||
doc.name = self.prefix + ('%0'+str(self.prefix_digits)+'d') % (counter + 1)
|
||||
frappe.db.set_value(self.doctype, self.name, 'counter', counter + 1)
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
class TestDocumentNamingRule(unittest.TestCase):
|
||||
def test_naming_rule_by_series(self):
|
||||
naming_rule = frappe.get_doc(dict(
|
||||
doctype = 'Document Naming Rule',
|
||||
document_type = 'ToDo',
|
||||
prefix = 'test-todo-',
|
||||
prefix_digits = 5
|
||||
)).insert()
|
||||
|
||||
todo = frappe.get_doc(dict(
|
||||
doctype = 'ToDo',
|
||||
description = 'Is this my name ' + frappe.generate_hash()
|
||||
)).insert()
|
||||
|
||||
self.assertEqual(todo.name, 'test-todo-00001')
|
||||
|
||||
naming_rule.delete()
|
||||
todo.delete()
|
||||
|
||||
def test_naming_rule_by_condition(self):
|
||||
naming_rule = frappe.get_doc(dict(
|
||||
doctype = 'Document Naming Rule',
|
||||
document_type = 'ToDo',
|
||||
prefix = 'test-high-',
|
||||
prefix_digits = 5,
|
||||
priority = 10,
|
||||
conditions = [dict(
|
||||
field = 'priority',
|
||||
condition = '=',
|
||||
value = 'High'
|
||||
)]
|
||||
)).insert()
|
||||
|
||||
# another rule
|
||||
naming_rule_1 = frappe.copy_doc(naming_rule)
|
||||
naming_rule_1.prefix = 'test-medium-'
|
||||
naming_rule_1.conditions[0].value = 'Medium'
|
||||
naming_rule_1.insert()
|
||||
|
||||
# default rule with low priority - should not get applied for rules
|
||||
# with higher priority
|
||||
naming_rule_2 = frappe.copy_doc(naming_rule)
|
||||
naming_rule_2.prefix = 'test-low-'
|
||||
naming_rule_2.priority = 0
|
||||
naming_rule_2.conditions = []
|
||||
naming_rule_2.insert()
|
||||
|
||||
|
||||
todo = frappe.get_doc(dict(
|
||||
doctype = 'ToDo',
|
||||
priority = 'High',
|
||||
description = 'Is this my name ' + frappe.generate_hash()
|
||||
)).insert()
|
||||
|
||||
todo_1 = frappe.get_doc(dict(
|
||||
doctype = 'ToDo',
|
||||
priority = 'Medium',
|
||||
description = 'Is this my name ' + frappe.generate_hash()
|
||||
)).insert()
|
||||
|
||||
todo_2 = frappe.get_doc(dict(
|
||||
doctype = 'ToDo',
|
||||
priority = 'Low',
|
||||
description = 'Is this my name ' + frappe.generate_hash()
|
||||
)).insert()
|
||||
|
||||
try:
|
||||
self.assertEqual(todo.name, 'test-high-00001')
|
||||
self.assertEqual(todo_1.name, 'test-medium-00001')
|
||||
self.assertEqual(todo_2.name, 'test-low-00001')
|
||||
finally:
|
||||
naming_rule.delete()
|
||||
naming_rule_1.delete()
|
||||
naming_rule_2.delete()
|
||||
todo.delete()
|
||||
todo_1.delete()
|
||||
todo_2.delete()
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) 2020, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Document Naming Rule Condition', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
});
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"actions": [],
|
||||
"creation": "2020-09-08 10:17:54.366279",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"field",
|
||||
"condition",
|
||||
"value"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "field",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Field",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "condition",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Condition",
|
||||
"options": "=\n!=\n>\n<\n>=\n<=",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "value",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Value",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-09-08 10:19:56.192949",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Document Naming Rule Condition",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class DocumentNamingRuleCondition(Document):
|
||||
pass
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestDocumentNamingRuleCondition(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -169,16 +169,14 @@ def get_comments(doctype, doc_name, frequency, user):
|
|||
return timeline
|
||||
|
||||
def is_document_followed(doctype, doc_name, user):
|
||||
docs = frappe.get_all(
|
||||
return frappe.db.exists(
|
||||
"Document Follow",
|
||||
filters={
|
||||
{
|
||||
"ref_doctype": doctype,
|
||||
"ref_docname": doc_name,
|
||||
"user": user
|
||||
},
|
||||
limit=1
|
||||
}
|
||||
)
|
||||
return len(docs)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_follow_users(doctype, doc_name):
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ def savedocs(doc, action):
|
|||
# update recent documents
|
||||
run_onload(doc)
|
||||
send_updated_docs(doc)
|
||||
|
||||
frappe.msgprint(frappe._("Saved"), indicator='green', alert=True)
|
||||
except Exception:
|
||||
frappe.errprint(frappe.utils.get_traceback())
|
||||
raise
|
||||
|
|
@ -36,6 +38,7 @@ def cancel(doctype=None, name=None, workflow_state_fieldname=None, workflow_stat
|
|||
doc.set(workflow_state_fieldname, workflow_state)
|
||||
doc.cancel()
|
||||
send_updated_docs(doc)
|
||||
frappe.msgprint(frappe._("Cancelled"), indicator='red', alert=True)
|
||||
|
||||
except Exception:
|
||||
frappe.errprint(frappe.utils.get_traceback())
|
||||
|
|
|
|||
|
|
@ -384,12 +384,14 @@ def build_xlsx_data(columns, data, visible_idx, include_indentation):
|
|||
|
||||
if isinstance(row, dict) and row:
|
||||
for idx in range(len(data.columns)):
|
||||
label = columns[idx]["label"]
|
||||
fieldname = columns[idx]["fieldname"]
|
||||
cell_value = row.get(fieldname, row.get(label, ""))
|
||||
if cint(include_indentation) and 'indent' in row and idx == 0:
|
||||
cell_value = (' ' * cint(row['indent'])) + cell_value
|
||||
row_data.append(cell_value)
|
||||
# check if column is not hidden
|
||||
if not columns[idx].get("hidden"):
|
||||
label = columns[idx]["label"]
|
||||
fieldname = columns[idx]["fieldname"]
|
||||
cell_value = row.get(fieldname, row.get(label, ""))
|
||||
if cint(include_indentation) and 'indent' in row and idx == 0:
|
||||
cell_value = (' ' * cint(row['indent'])) + cell_value
|
||||
row_data.append(cell_value)
|
||||
else:
|
||||
row_data = row
|
||||
|
||||
|
|
|
|||
|
|
@ -1,181 +1,78 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2019-01-09 16:39:23.746535",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"actions": [],
|
||||
"creation": "2019-01-09 16:39:23.746535",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"ref_doctype",
|
||||
"ref_docname",
|
||||
"user"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "ref_doctype",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Doctype",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "ref_doctype",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Doctype",
|
||||
"options": "DocType",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "ref_docname",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Document Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "ref_doctype",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "ref_docname",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Document Name",
|
||||
"options": "ref_doctype",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "user",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "User",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "User",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"fieldname": "user",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "User",
|
||||
"options": "User",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 1,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-02-26 15:43:44.330348",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Email",
|
||||
"name": "Document Follow",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"in_create": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2020-09-17 09:19:28.496453",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Email",
|
||||
"name": "Document Follow",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 1,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
],
|
||||
"read_only": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
|
|
@ -335,6 +335,9 @@ class BaseDocument(object):
|
|||
if frappe.db.is_primary_key_violation(e):
|
||||
if self.meta.autoname=="hash":
|
||||
# hash collision? try again
|
||||
frappe.flags.retry_count = (frappe.flags.retry_count or 0) + 1
|
||||
if frappe.flags.retry_count > 5:
|
||||
raise
|
||||
self.name = None
|
||||
self.db_insert()
|
||||
return
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from frappe import _
|
|||
from frappe.utils import now_datetime, cint, cstr
|
||||
import re
|
||||
from six import string_types
|
||||
from frappe.model import log_types
|
||||
|
||||
|
||||
def set_new_name(doc):
|
||||
|
|
@ -35,7 +36,13 @@ def set_new_name(doc):
|
|||
elif getattr(doc.meta, "issingle", False):
|
||||
doc.name = doc.doctype
|
||||
|
||||
else:
|
||||
elif getattr(doc.meta, "istable", False):
|
||||
doc.name = make_autoname("hash", doc.doctype)
|
||||
|
||||
if not doc.name:
|
||||
set_naming_from_document_naming_rule(doc)
|
||||
|
||||
if not doc.name:
|
||||
doc.run_method("autoname")
|
||||
|
||||
if not doc.name and autoname:
|
||||
|
|
@ -43,12 +50,15 @@ def set_new_name(doc):
|
|||
|
||||
# if the autoname option is 'field:' and no name was derived, we need to
|
||||
# notify
|
||||
if autoname.startswith("field:") and not doc.name:
|
||||
if not doc.name and autoname.startswith("field:"):
|
||||
fieldname = autoname[6:]
|
||||
frappe.throw(_("{0} is required").format(doc.meta.get_label(fieldname)))
|
||||
|
||||
# at this point, we fall back to name generation with the hash option
|
||||
if not doc.name or autoname == "hash":
|
||||
if not doc.name and autoname == "hash":
|
||||
doc.name = make_autoname("hash", doc.doctype)
|
||||
|
||||
if not doc.name:
|
||||
doc.name = make_autoname("hash", doc.doctype)
|
||||
|
||||
doc.name = validate_name(
|
||||
|
|
@ -76,6 +86,23 @@ def set_name_from_naming_options(autoname, doc):
|
|||
elif "#" in autoname:
|
||||
doc.name = make_autoname(autoname, doc=doc)
|
||||
|
||||
def set_naming_from_document_naming_rule(doc):
|
||||
'''
|
||||
Evaluate rules based on "Document Naming Series" doctype
|
||||
'''
|
||||
if doc.doctype in log_types:
|
||||
return
|
||||
|
||||
try:
|
||||
for d in frappe.get_all('Document Naming Rule',
|
||||
dict(document_type=doc.doctype, disabled=0), order_by='priority desc'):
|
||||
frappe.get_cached_doc('Document Naming Rule', d.name).apply(doc)
|
||||
if doc.name:
|
||||
break
|
||||
except frappe.db.TableMissingError: # noqa: E722
|
||||
# not yet bootstrapped
|
||||
pass
|
||||
|
||||
def set_name_by_naming_series(doc):
|
||||
"""Sets name by the `naming_series` property"""
|
||||
if not doc.naming_series:
|
||||
|
|
|
|||
|
|
@ -306,6 +306,7 @@ frappe.patches.v13_0.add_toggle_width_in_navbar_settings
|
|||
frappe.patches.v13_0.rename_notification_fields
|
||||
frappe.patches.v13_0.remove_duplicate_navbar_items
|
||||
frappe.patches.v12_0.set_default_password_reset_limit
|
||||
execute:frappe.reload_doc('core', 'doctype', 'doctype', force=True)
|
||||
frappe.patches.v13_0.set_route_for_blog_category
|
||||
frappe.patches.v13_0.enable_custom_script
|
||||
frappe.patches.v13_0.update_newsletter_content_type
|
||||
|
|
|
|||
|
|
@ -243,6 +243,7 @@
|
|||
"public/js/frappe/utils/energy_point_utils.js",
|
||||
"public/js/frappe/utils/dashboard_utils.js",
|
||||
"public/js/frappe/ui/chart.js",
|
||||
"public/js/frappe/ui/datatable.js",
|
||||
"public/js/frappe/ui/driver.js",
|
||||
"public/js/frappe/barcode_scanner/index.js"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1265,7 +1265,7 @@ frappe.ui.form.Form = class FrappeForm {
|
|||
|
||||
set_df_property(fieldname, property, value, docname, table_field) {
|
||||
var df;
|
||||
if (!docname && !table_field){
|
||||
if (!docname && !table_field) {
|
||||
df = this.get_docfield(fieldname);
|
||||
} else {
|
||||
var grid = this.fields_dict[table_field].grid,
|
||||
|
|
@ -1273,7 +1273,7 @@ frappe.ui.form.Form = class FrappeForm {
|
|||
if (fname && fname.length)
|
||||
df = frappe.meta.get_docfield(fname[0].parent, fieldname, docname);
|
||||
}
|
||||
if(df && df[property] != value) {
|
||||
if (df && df[property] != value) {
|
||||
df[property] = value;
|
||||
refresh_field(fieldname, table_field);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -215,10 +215,6 @@ frappe.ui.form.save = function (frm, action, callback, btn) {
|
|||
$(btn).prop("disabled", false);
|
||||
frappe.ui.form.is_saving = false;
|
||||
|
||||
if (!r.exc) {
|
||||
frappe.show_alert({message: __('Saved'), indicator: 'green'});
|
||||
}
|
||||
|
||||
if (r) {
|
||||
var doc = r.docs && r.docs[0];
|
||||
if (doc) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
frappe.provide('frappe.route');
|
||||
frappe.route_history_queue = [];
|
||||
const routes_to_skip = ['Form', 'social', 'setup-wizard'];
|
||||
const routes_to_skip = ['Form', 'social', 'setup-wizard', 'recorder'];
|
||||
|
||||
const save_routes = frappe.utils.debounce(() => {
|
||||
const routes = frappe.route_history_queue;
|
||||
|
|
@ -30,7 +30,6 @@ function is_route_useful(route) {
|
|||
if (!route[1]) {
|
||||
return false;
|
||||
} else if ((route[0] === 'List' && !route[2]) || routes_to_skip.includes(route[0])) {
|
||||
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
|
|
|
|||
3
frappe/public/js/frappe/ui/datatable.js
Normal file
3
frappe/public/js/frappe/ui/datatable.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import DataTable from "frappe-datatable";
|
||||
|
||||
frappe.DataTable = DataTable;
|
||||
|
|
@ -621,28 +621,6 @@ def parse_json(val):
|
|||
val = frappe._dict(val)
|
||||
return val
|
||||
|
||||
def cast_fieldtype(fieldtype, value):
|
||||
if fieldtype in ("Currency", "Float", "Percent"):
|
||||
value = flt(value)
|
||||
|
||||
elif fieldtype in ("Int", "Check"):
|
||||
value = cint(value)
|
||||
|
||||
elif fieldtype in ("Data", "Text", "Small Text", "Long Text",
|
||||
"Text Editor", "Select", "Link", "Dynamic Link"):
|
||||
value = cstr(value)
|
||||
|
||||
elif fieldtype == "Date":
|
||||
value = getdate(value)
|
||||
|
||||
elif fieldtype == "Datetime":
|
||||
value = get_datetime(value)
|
||||
|
||||
elif fieldtype == "Time":
|
||||
value = to_timedelta(value)
|
||||
|
||||
return value
|
||||
|
||||
def get_db_count(*args):
|
||||
"""
|
||||
Pass a doctype or a series of doctypes to get the count of docs in them
|
||||
|
|
|
|||
|
|
@ -411,6 +411,28 @@ def has_common(l1, l2):
|
|||
"""Returns truthy value if there are common elements in lists l1 and l2"""
|
||||
return set(l1) & set(l2)
|
||||
|
||||
def cast_fieldtype(fieldtype, value):
|
||||
if fieldtype in ("Currency", "Float", "Percent"):
|
||||
value = flt(value)
|
||||
|
||||
elif fieldtype in ("Int", "Check"):
|
||||
value = cint(value)
|
||||
|
||||
elif fieldtype in ("Data", "Text", "Small Text", "Long Text",
|
||||
"Text Editor", "Select", "Link", "Dynamic Link"):
|
||||
value = cstr(value)
|
||||
|
||||
elif fieldtype == "Date":
|
||||
value = getdate(value)
|
||||
|
||||
elif fieldtype == "Datetime":
|
||||
value = get_datetime(value)
|
||||
|
||||
elif fieldtype == "Time":
|
||||
value = to_timedelta(value)
|
||||
|
||||
return value
|
||||
|
||||
def flt(s, precision=None):
|
||||
"""Convert to float (ignore commas)"""
|
||||
if isinstance(s, string_types):
|
||||
|
|
@ -1017,20 +1039,22 @@ def evaluate_filters(doc, filters):
|
|||
if isinstance(filters, dict):
|
||||
for key, value in iteritems(filters):
|
||||
f = get_filter(None, {key:value})
|
||||
if not compare(doc.get(f.fieldname), f.operator, f.value):
|
||||
if not compare(doc.get(f.fieldname), f.operator, f.value, f.fieldtype):
|
||||
return False
|
||||
|
||||
elif isinstance(filters, (list, tuple)):
|
||||
for d in filters:
|
||||
f = get_filter(None, d)
|
||||
if not compare(doc.get(f.fieldname), f.operator, f.value):
|
||||
if not compare(doc.get(f.fieldname), f.operator, f.value, f.fieldtype):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def compare(val1, condition, val2):
|
||||
def compare(val1, condition, val2, fieldtype=None):
|
||||
ret = False
|
||||
if fieldtype:
|
||||
val2 = cast_fieldtype(fieldtype, val2)
|
||||
if condition in operator_map:
|
||||
ret = operator_map[condition](val1, val2)
|
||||
|
||||
|
|
@ -1044,6 +1068,7 @@ def get_filter(doctype, f, filters_config=None):
|
|||
"fieldname":
|
||||
"operator":
|
||||
"value":
|
||||
"fieldtype":
|
||||
}
|
||||
"""
|
||||
from frappe.model import default_fields, optional_fields
|
||||
|
|
@ -1095,6 +1120,13 @@ def get_filter(doctype, f, filters_config=None):
|
|||
f.doctype = df.options
|
||||
break
|
||||
|
||||
try:
|
||||
df = frappe.get_meta(f.doctype).get_field(f.fieldname)
|
||||
except frappe.exceptions.DoesNotExistError:
|
||||
df = None
|
||||
|
||||
f.fieldtype = df.fieldtype if df else None
|
||||
|
||||
return f
|
||||
|
||||
def make_filter_tuple(doctype, key, value):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue