Merge branch 'develop' of https://github.com/frappe/frappe into rebrand-ui-fixes
This commit is contained in:
commit
96f0bab661
25 changed files with 104 additions and 69 deletions
|
|
@ -1,8 +1,14 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
"""
|
||||
globals attached to frappe module
|
||||
+ some utility functions that should probably be moved
|
||||
Frappe - Low Code Open Source Framework in Python and JS
|
||||
|
||||
Frappe, pronounced fra-pay, is a full stack, batteries-included, web
|
||||
framework written in Python and Javascript with MariaDB as the database.
|
||||
It is the framework which powers ERPNext. It is pretty generic and can
|
||||
be used to build database driven apps.
|
||||
|
||||
Read the documentation: https://frappeframework.com/docs
|
||||
"""
|
||||
from __future__ import unicode_literals, print_function
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ from frappe.model.document import Document
|
|||
from frappe.core.doctype.communication.email import make
|
||||
from frappe.utils.background_jobs import get_jobs
|
||||
from frappe.automation.doctype.assignment_rule.assignment_rule import get_repeated
|
||||
from frappe.contacts.doctype.contact.contact import get_contacts_linked_from
|
||||
from frappe.contacts.doctype.contact.contact import get_contacts_linking_to
|
||||
|
||||
month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12}
|
||||
week_map = {'Monday': 0, 'Tuesday': 1, 'Wednesday': 2, 'Thursday': 3, 'Friday': 4, 'Saturday': 5, 'Sunday': 6}
|
||||
|
|
@ -328,13 +330,8 @@ class AutoRepeat(Document):
|
|||
|
||||
def fetch_linked_contacts(self):
|
||||
if self.reference_doctype and self.reference_document:
|
||||
res = frappe.db.get_all('Contact',
|
||||
fields=['email_id'],
|
||||
filters=[
|
||||
['Dynamic Link', 'link_doctype', '=', self.reference_doctype],
|
||||
['Dynamic Link', 'link_name', '=', self.reference_document]
|
||||
])
|
||||
|
||||
res = get_contacts_linking_to(self.reference_doctype, self.reference_document, fields=['email_id'])
|
||||
res += get_contacts_linked_from(self.reference_doctype, self.reference_document, fields=['email_id'])
|
||||
email_ids = list(set([d.email_id for d in res]))
|
||||
if not email_ids:
|
||||
frappe.msgprint(_('No contacts linked to document'), alert=True)
|
||||
|
|
|
|||
|
|
@ -256,3 +256,27 @@ def get_contact_with_phone_number(number):
|
|||
def get_contact_name(email_id):
|
||||
contact = frappe.get_list("Contact Email", filters={"email_id": email_id}, fields=["parent"], limit=1)
|
||||
return contact[0].parent if contact else None
|
||||
|
||||
def get_contacts_linking_to(doctype, docname, fields=None):
|
||||
"""Return a list of contacts containing a link to the given document."""
|
||||
return frappe.get_list('Contact', fields=fields, filters=[
|
||||
['Dynamic Link', 'link_doctype', '=', doctype],
|
||||
['Dynamic Link', 'link_name', '=', docname]
|
||||
])
|
||||
|
||||
def get_contacts_linked_from(doctype, docname, fields=None):
|
||||
"""Return a list of contacts that are contained in (linked from) the given document."""
|
||||
link_fields = frappe.get_meta(doctype).get('fields', {
|
||||
'fieldtype': 'Link',
|
||||
'options': 'Contact'
|
||||
})
|
||||
if not link_fields:
|
||||
return []
|
||||
|
||||
contact_names = frappe.get_value(doctype, docname, fieldname=[f.fieldname for f in link_fields])
|
||||
if not contact_names:
|
||||
return []
|
||||
|
||||
return frappe.get_list('Contact', fields=fields, filters={
|
||||
'name': ('in', contact_names)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -656,7 +656,7 @@ class DocType(Document):
|
|||
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):
|
||||
if re.search("^[ \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
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@
|
|||
"no_copy": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"default": "1",
|
||||
"fieldname": "logout_all_sessions",
|
||||
"fieldtype": "Check",
|
||||
"label": "Logout From All Devices After Changing Password"
|
||||
|
|
@ -669,7 +669,7 @@
|
|||
}
|
||||
],
|
||||
"max_attachments": 5,
|
||||
"modified": "2021-01-02 11:21:50.507786",
|
||||
"modified": "2021-02-01 16:11:06.037543",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "User",
|
||||
|
|
|
|||
|
|
@ -562,6 +562,10 @@ def get_perm_info(role):
|
|||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def update_password(new_password, logout_all_sessions=0, key=None, old_password=None):
|
||||
#validate key to avoid key input like ['like', '%'], '', ['in', ['']]
|
||||
if key and not isinstance(key, str):
|
||||
frappe.throw(_('Invalid key type'))
|
||||
|
||||
result = test_password_strength(new_password, key, old_password)
|
||||
feedback = result.get("feedback", None)
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ def get_result(doc, filters, to_date=None):
|
|||
filters = frappe.parse_json(filters)
|
||||
|
||||
if not filters:
|
||||
filters = []
|
||||
filters = []
|
||||
|
||||
if to_date:
|
||||
filters.append([doc.document_type, 'creation', '<', to_date])
|
||||
|
|
@ -107,9 +107,13 @@ def get_percentage_difference(doc, filters, result):
|
|||
return
|
||||
|
||||
previous_result = calculate_previous_result(doc, filters)
|
||||
difference = (result - previous_result)/100.0
|
||||
|
||||
return difference
|
||||
if previous_result == 0:
|
||||
return None
|
||||
else:
|
||||
if result == previous_result:
|
||||
return 0
|
||||
else:
|
||||
return ((result/previous_result)-1)*100.0
|
||||
|
||||
|
||||
def calculate_previous_result(doc, filters):
|
||||
|
|
@ -197,4 +201,4 @@ def add_card_to_dashboard(args):
|
|||
card.save()
|
||||
|
||||
dashboard.append('cards', dashboard_link)
|
||||
dashboard.save()
|
||||
dashboard.save()
|
||||
|
|
|
|||
|
|
@ -297,8 +297,9 @@ def inline_style_in_html(html):
|
|||
|
||||
for app in apps:
|
||||
path = 'assets/{0}/css/email.css'.format(app)
|
||||
if os.path.exists(os.path.abspath(path)):
|
||||
css_files.append(path)
|
||||
css_files.append(path)
|
||||
|
||||
css_files = [css_file for css_file in css_files if os.path.exists(os.path.abspath(css_file))]
|
||||
|
||||
p = Premailer(html=html, external_styles=css_files, strip_important=False)
|
||||
|
||||
|
|
|
|||
|
|
@ -2729,11 +2729,11 @@
|
|||
},
|
||||
"Zimbabwe": {
|
||||
"code": "zw",
|
||||
"currency": "ZWD",
|
||||
"currency_fraction": "Thebe",
|
||||
"currency": "ZWL",
|
||||
"currency_fraction": "Cent",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_name": "Zimbabwe Dollar",
|
||||
"currency_symbol": "P",
|
||||
"currency_symbol": "ZWL$",
|
||||
"number_format": "# ###.##",
|
||||
"timezones": [
|
||||
"Africa/Harare"
|
||||
|
|
|
|||
|
|
@ -58,6 +58,11 @@ website_route_rules = [
|
|||
{"from_route": "/kb/<category>", "to_route": "Help Article"},
|
||||
{"from_route": "/newsletters", "to_route": "Newsletter"},
|
||||
{"from_route": "/profile", "to_route": "me"},
|
||||
{"from_route": "/app/<path:app_path>", "to_route": "app"},
|
||||
]
|
||||
|
||||
website_redirects = [
|
||||
{"source": r"/desk(.*)", "target": r"/app\1"},
|
||||
]
|
||||
|
||||
base_template = "templates/base.html"
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ def sync_for(app_name, force=0, sync_everything = False, verbose=False, reset_pe
|
|||
|
||||
for module_name in frappe.local.app_modules.get(app_name) or []:
|
||||
folder = os.path.dirname(frappe.get_module(app_name + "." + module_name).__file__)
|
||||
get_doc_files(files, folder, force, sync_everything, verbose=verbose)
|
||||
get_doc_files(files, folder)
|
||||
|
||||
l = len(files)
|
||||
if l:
|
||||
|
|
@ -77,7 +77,7 @@ def sync_for(app_name, force=0, sync_everything = False, verbose=False, reset_pe
|
|||
# print each progress bar on new line
|
||||
print()
|
||||
|
||||
def get_doc_files(files, start_path, force=0, sync_everything = False, verbose=False):
|
||||
def get_doc_files(files, start_path):
|
||||
"""walk and sync all doctypes and pages"""
|
||||
|
||||
# load in sequence - warning for devs
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ frappe.patches.v9_1.resave_domain_settings
|
|||
frappe.patches.v9_1.revert_domain_settings
|
||||
frappe.patches.v9_1.move_feed_to_activity_log
|
||||
execute:frappe.delete_doc('Page', 'data-import-tool', ignore_missing=True)
|
||||
frappe.patches.v10_0.reload_countries_and_currencies # 14-10-2020
|
||||
frappe.patches.v10_0.reload_countries_and_currencies # 2021-02-03
|
||||
frappe.patches.v10_0.refactor_social_login_keys
|
||||
frappe.patches.v10_0.enable_chat_by_default_within_system_settings
|
||||
frappe.patches.v10_0.remove_custom_field_for_disabled_domain
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ def execute():
|
|||
rename_doc('DocType', 'Desk Shortcut', 'Workspace Shortcut', ignore_if_exists=True)
|
||||
rename_doc('DocType', 'Desk Link', 'Workspace Link', ignore_if_exists=True)
|
||||
|
||||
frappe.reload_doc('desk', 'doctype', 'workspace')
|
||||
frappe.reload_doc('desk', 'doctype', 'workspace_link')
|
||||
frappe.reload_doc('desk', 'doctype', 'workspace_chart')
|
||||
frappe.reload_doc('desk', 'doctype', 'workspace_shortcut')
|
||||
frappe.reload_doc('desk', 'doctype', 'workspace', force=True)
|
||||
frappe.reload_doc('desk', 'doctype', 'workspace_link', force=True)
|
||||
frappe.reload_doc('desk', 'doctype', 'workspace_chart', force=True)
|
||||
frappe.reload_doc('desk', 'doctype', 'workspace_shortcut', force=True)
|
||||
|
|
|
|||
|
|
@ -1305,8 +1305,6 @@ class {
|
|||
this.set_wrapper(selector ? selector : "body")
|
||||
this.set_options(options)
|
||||
|
||||
// Load Emojis.
|
||||
frappe.chat.emoji()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -127,13 +127,6 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
|
|||
let display_value = frappe.format(value, this.df, { no_icon: true, inline: true }, doc);
|
||||
this.disp_area && $(this.disp_area).html(display_value);
|
||||
},
|
||||
|
||||
bind_change_event: function() {
|
||||
var me = this;
|
||||
this.$input && this.$input.on("change", this.change || function(e) {
|
||||
me.parse_validate_and_set_in_model(me.get_input_value(), e);
|
||||
});
|
||||
},
|
||||
set_label: function(label) {
|
||||
if(label) this.df.label = label;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ frappe.provide('frappe.phone_call');
|
|||
frappe.ui.form.ControlData = frappe.ui.form.ControlInput.extend({
|
||||
html_element: "input",
|
||||
input_type: "text",
|
||||
trigger_change_on_input_event: true,
|
||||
make_input: function() {
|
||||
if(this.$input) return;
|
||||
|
||||
|
|
@ -22,8 +23,20 @@ frappe.ui.form.ControlData = frappe.ui.form.ControlInput.extend({
|
|||
this.has_input = true;
|
||||
this.bind_change_event();
|
||||
this.setup_autoname_check();
|
||||
// somehow this event does not bubble up to document
|
||||
// after v7, if you can debug, remove this
|
||||
},
|
||||
bind_change_event: function() {
|
||||
const change_handler = e => {
|
||||
if (this.change) this.change(e);
|
||||
else {
|
||||
let value = this.get_input_value();
|
||||
this.parse_validate_and_set_in_model(value, e);
|
||||
}
|
||||
};
|
||||
this.$input.on("change", change_handler);
|
||||
if (this.trigger_change_on_input_event) {
|
||||
// debounce to avoid repeated validations on value change
|
||||
this.$input.on("input", frappe.utils.debounce(change_handler, 500));
|
||||
}
|
||||
},
|
||||
setup_autoname_check: function() {
|
||||
if (!this.df.parent) return;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
frappe.ui.form.ControlDate = frappe.ui.form.ControlData.extend({
|
||||
trigger_change_on_input_event: false,
|
||||
make_input: function() {
|
||||
this._super();
|
||||
this.make_picker();
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import Awesomplete from 'awesomplete';
|
|||
frappe.ui.form.recent_link_validations = {};
|
||||
|
||||
frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({
|
||||
trigger_change_on_input_event: false,
|
||||
make_input: function() {
|
||||
var me = this;
|
||||
// line-height: 1 is for Mozilla 51, shows extra padding otherwise
|
||||
|
|
|
|||
|
|
@ -564,13 +564,8 @@ frappe.ui.form.Form = class FrappeForm {
|
|||
let me = this;
|
||||
return new Promise((resolve, reject) => {
|
||||
btn && $(btn).prop("disabled", true);
|
||||
$(document.activeElement).blur();
|
||||
|
||||
frappe.ui.form.close_grid_form();
|
||||
// let any pending js process finish
|
||||
setTimeout(function() {
|
||||
me.validate_and_save(save_action, callback, btn, on_error, resolve, reject);
|
||||
}, 100);
|
||||
me.validate_and_save(save_action, callback, btn, on_error, resolve, reject);
|
||||
}).then(() => {
|
||||
me.show_success_action();
|
||||
}).catch((e) => {
|
||||
|
|
|
|||
|
|
@ -152,9 +152,13 @@ frappe.ui.form.LinkSelector = Class.extend({
|
|||
d = me.target.add_new_row();
|
||||
},
|
||||
() => frappe.timeout(0.1),
|
||||
() => frappe.model.set_value(d.doctype, d.name, me.fieldname, value),
|
||||
() => frappe.timeout(0.5),
|
||||
() => frappe.model.set_value(d.doctype, d.name, me.qty_fieldname, data.qty),
|
||||
() => {
|
||||
let args = {};
|
||||
args[me.fieldname] = value;
|
||||
args[me.qty_fieldname] = data.qty;
|
||||
|
||||
return frappe.model.set_value(d.doctype, d.name, args);
|
||||
},
|
||||
() => frappe.show_alert(__("Added {0} ({1})", [value, data.qty]))
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -362,9 +362,7 @@ $.extend(frappe.model, {
|
|||
);
|
||||
} else if (!opts.source_name && opts.frm) {
|
||||
opts.source_name = opts.frm.doc.name;
|
||||
|
||||
// Allow opening a mapped doc without a source document name
|
||||
} else if (!opts.frm) {
|
||||
} else if (!opts.frm && !opts.source_name) {
|
||||
opts.source_name = null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -191,9 +191,9 @@ window.strip = function (s, chars) {
|
|||
window.lstrip = function lstrip(s, chars) {
|
||||
if (!chars) chars = ['\n', '\t', ' '];
|
||||
// strip left
|
||||
var first_char = s.substr(0, 1);
|
||||
let first_char = s.substr(0, 1);
|
||||
while (in_list(chars, first_char)) {
|
||||
var s = s.substr(1);
|
||||
s = s.substr(1);
|
||||
first_char = s.substr(0, 1);
|
||||
}
|
||||
return s;
|
||||
|
|
@ -201,9 +201,9 @@ window.lstrip = function lstrip(s, chars) {
|
|||
|
||||
window.rstrip = function (s, chars) {
|
||||
if (!chars) chars = ['\n', '\t', ' '];
|
||||
var last_char = s.substr(s.length - 1);
|
||||
let last_char = s.substr(s.length - 1);
|
||||
while (in_list(chars, last_char)) {
|
||||
let s = s.substr(0, s.length - 1);
|
||||
s = s.substr(0, s.length - 1);
|
||||
last_char = s.substr(s.length - 1);
|
||||
}
|
||||
return s;
|
||||
|
|
|
|||
|
|
@ -231,9 +231,7 @@ export default class NumberCardWidget extends Widget {
|
|||
let color_class = '';
|
||||
|
||||
return this.get_percentage_stats().then(() => {
|
||||
if (this.percentage_stat == undefined) return;
|
||||
|
||||
if (this.percentage_stat == 0) {
|
||||
if (this.percentage_stat == 0 || this.percentage_stat == undefined) {
|
||||
color_class = 'grey-stat';
|
||||
} else if (this.percentage_stat > 0) {
|
||||
caret_html =
|
||||
|
|
@ -258,6 +256,7 @@ export default class NumberCardWidget extends Widget {
|
|||
const stats_qualifier = stats_qualifier_map[this.card_doc.stats_time_interval];
|
||||
|
||||
let get_stat = () => {
|
||||
if (this.percentage_stat == undefined) return NaN;
|
||||
const parts = this.percentage_stat.split(' ');
|
||||
const symbol = parts[1] || '';
|
||||
return Math.abs(parts[0]) + ' ' + symbol;
|
||||
|
|
|
|||
|
|
@ -252,13 +252,6 @@ def resolve_path(path):
|
|||
if path != "index":
|
||||
path = resolve_from_map(path)
|
||||
|
||||
if path.startswith("app"):
|
||||
path = "app"
|
||||
|
||||
# to keep backward compatibility
|
||||
if path.startswith("desk"):
|
||||
path = "app"
|
||||
|
||||
return path
|
||||
|
||||
def resolve_from_map(path):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
Babel==2.6.0
|
||||
beautifulsoup4==4.8.2
|
||||
bleach-whitelist==0.0.10
|
||||
bleach==3.1.4
|
||||
bleach==3.3.0
|
||||
boto3==1.10.18
|
||||
braintree==3.57.1
|
||||
chardet==3.0.4
|
||||
|
|
@ -27,7 +27,7 @@ html5lib==1.0.1
|
|||
ipython==7.14.0
|
||||
Jinja2==2.11.3
|
||||
ldap3==2.7
|
||||
markdown2==2.3.9
|
||||
markdown2==2.4.0
|
||||
maxminddb-geolite2==2018.703
|
||||
ndg-httpsclient==0.5.1
|
||||
num2words==0.5.10
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue