Merge branch 'develop'
This commit is contained in:
commit
00ae80134f
30 changed files with 146 additions and 69 deletions
|
|
@ -1,5 +1,9 @@
|
|||
# Contributing to Frappe / ERPNext
|
||||
|
||||
### Update 16-Sep-14
|
||||
|
||||
Please send pull requests to branch v5.0
|
||||
|
||||
## Reporting issues
|
||||
|
||||
We only accept issues that are bug reports or feature requests. Bugs must be isolated and reproducible problems. Please read the following guidelines before opening any issue.
|
||||
|
|
|
|||
|
|
@ -549,9 +549,15 @@ def import_doc(path, ignore_links=False, ignore_insert=False, insert=False):
|
|||
from frappe.core.page.data_import_tool import data_import_tool
|
||||
data_import_tool.import_doc(path, ignore_links=ignore_links, ignore_insert=ignore_insert, insert=insert)
|
||||
|
||||
def copy_doc(doc):
|
||||
def copy_doc(doc, ignore_no_copy=True):
|
||||
""" No_copy fields also get copied."""
|
||||
import copy
|
||||
|
||||
def remove_no_copy_fields(d):
|
||||
for df in d.meta.get("fields", {"no_copy": 1}):
|
||||
if hasattr(d, df.fieldname):
|
||||
d.set(df.fieldname, None)
|
||||
|
||||
if not isinstance(doc, dict):
|
||||
d = doc.as_dict()
|
||||
else:
|
||||
|
|
@ -564,12 +570,18 @@ def copy_doc(doc):
|
|||
newdoc.creation = None
|
||||
newdoc.amended_from = None
|
||||
newdoc.amendment_date = None
|
||||
if not ignore_no_copy:
|
||||
remove_no_copy_fields(newdoc)
|
||||
|
||||
for d in newdoc.get_all_children():
|
||||
d.name = None
|
||||
d.parent = None
|
||||
d.set("__islocal", 1)
|
||||
d.owner = None
|
||||
d.creation = None
|
||||
if not ignore_no_copy:
|
||||
remove_no_copy_fields(d)
|
||||
|
||||
return newdoc
|
||||
|
||||
def compare(val1, condition, val2):
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
__version__ = "4.3.0"
|
||||
__version__ = "4.4.0"
|
||||
|
|
|
|||
|
|
@ -117,6 +117,11 @@ class CustomizeForm(Document):
|
|||
if property == "fieldtype":
|
||||
self.validate_fieldtype_change(df, meta_df[0].get(property), df.get(property))
|
||||
|
||||
elif property == "allow_on_submit" and df.get(property):
|
||||
frappe.msgprint(_("Row {0}: Not allowed to enable Allow on Submit for standard fields")\
|
||||
.format(df.idx))
|
||||
continue
|
||||
|
||||
self.make_property_setter(property=property, value=df.get(property),
|
||||
property_type=self.docfield_properties[property], fieldname=df.fieldname)
|
||||
|
||||
|
|
|
|||
|
|
@ -159,3 +159,17 @@ class TestCustomizeForm(unittest.TestCase):
|
|||
|
||||
frappe.local.test_objects["Property Setter"] = []
|
||||
make_test_records_for_doctype("Property Setter")
|
||||
|
||||
def test_set_allow_on_submit(self):
|
||||
d = self.get_customize_form("User")
|
||||
d.get("customize_form_fields", {"fieldname": "first_name"})[0].allow_on_submit = 1
|
||||
d.get("customize_form_fields", {"fieldname": "test_custom_field"})[0].allow_on_submit = 1
|
||||
d.run_method("save_customization")
|
||||
|
||||
d = self.get_customize_form("User")
|
||||
|
||||
# don't allow for standard fields
|
||||
self.assertEquals(d.get("customize_form_fields", {"fieldname": "first_name"})[0].allow_on_submit or 0, 0)
|
||||
|
||||
# allow for custom field
|
||||
self.assertEquals(d.get("customize_form_fields", {"fieldname": "test_custom_field"})[0].allow_on_submit, 1)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class FileData(Document):
|
|||
def before_insert(self):
|
||||
frappe.local.rollback_observers.append(self)
|
||||
|
||||
def on_update(self):
|
||||
def validate(self):
|
||||
if not getattr(self, "ignore_duplicate_entry_error", False):
|
||||
# check duplicate assignement
|
||||
n_records = frappe.db.sql("""select name from `tabFile Data`
|
||||
|
|
@ -28,8 +28,7 @@ class FileData(Document):
|
|||
self.attached_to_name))
|
||||
if len(n_records) > 0:
|
||||
self.duplicate_entry = n_records[0][0]
|
||||
frappe.msgprint(frappe._("Same file has already been attached to the record"))
|
||||
raise frappe.DuplicateEntryError
|
||||
frappe.throw(frappe._("Same file has already been attached to the record"), frappe.DuplicateEntryError)
|
||||
|
||||
def on_trash(self):
|
||||
if self.attached_to_name:
|
||||
|
|
|
|||
|
|
@ -89,7 +89,15 @@ def clear_notifications(user=None):
|
|||
|
||||
def delete_notification_count_for(doctype):
|
||||
if frappe.flags.in_import: return
|
||||
frappe.db.sql("""delete from `tabNotification Count` where for_doctype = %s""", (doctype,))
|
||||
|
||||
try:
|
||||
frappe.db.sql("""delete from `tabNotification Count` where for_doctype = %s""", (doctype,))
|
||||
|
||||
except MySQLdb.OperationalError, e:
|
||||
if e.args[0] != 1213:
|
||||
raise
|
||||
|
||||
logger.error("Deadlock")
|
||||
|
||||
def clear_doctype_notifications(doc, method=None, *args, **kwargs):
|
||||
if frappe.flags.in_import:
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
</div>
|
||||
<div class="col-xs-1 text-right" style="margin-bottom: 3px;">
|
||||
<span class="filterable" data-filter="owner,=,{%= doc.owner %}">
|
||||
{%= frappe.avatar(doc.assigned_to) %}
|
||||
{%= frappe.avatar(doc.owner) %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
from __future__ import unicode_literals
|
||||
import MySQLdb
|
||||
from markdown2 import UnicodeWithAttrs
|
||||
import warnings
|
||||
import datetime
|
||||
import frappe
|
||||
|
|
@ -48,6 +49,7 @@ class Database:
|
|||
use_unicode=True, charset='utf8')
|
||||
self._conn.converter[246]=float
|
||||
self._conn.converter[12]=get_datetime
|
||||
self._conn.encoders[UnicodeWithAttrs] = self._conn.encoders[unicode]
|
||||
|
||||
self._cursor = self._conn.cursor()
|
||||
if self.user != 'root':
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
app_name = "frappe"
|
||||
app_title = "Frappe Framework"
|
||||
app_publisher = "Web Notes Technologies Pvt. Ltd."
|
||||
app_description = "Full Stack Web Application Framwork in Python"
|
||||
app_description = "Full Stack Web Application Framework in Python"
|
||||
app_icon = "assets/frappe/images/frappe.svg"
|
||||
app_version = "4.3.0"
|
||||
app_version = "4.4.0"
|
||||
app_color = "#3498db"
|
||||
app_email = "support@frappe.io"
|
||||
|
||||
|
|
|
|||
|
|
@ -89,5 +89,5 @@ def get_default_value(df, defaults, user_permissions, parent_doc):
|
|||
elif df.fieldtype == "Time":
|
||||
return nowtime()
|
||||
|
||||
elif (df.fieldtype == "Select" and df.options and df.options != "[Select]"):
|
||||
elif (df.fieldtype == "Select" and df.options and df.options not in ("[Select]", "Loading...")):
|
||||
return df.options.split("\n")[0]
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ def check_if_doc_is_dynamically_linked(doc):
|
|||
if frappe.get_meta(df.parent).issingle:
|
||||
|
||||
# dynamic link in single doc
|
||||
refdoc = frappe.get_singles_dict(df.parent)
|
||||
refdoc = frappe.db.get_singles_dict(df.parent)
|
||||
if refdoc.get(df.options)==doc.doctype and refdoc.get(df.fieldname)==doc.name:
|
||||
frappe.throw(_("Cannot delete or cancel because {0} {1} is linked with {2} {3}").format(doc.doctype,
|
||||
doc.name, df.parent, ""), frappe.LinkExistsError)
|
||||
|
|
|
|||
|
|
@ -80,7 +80,13 @@ class Document(BaseDocument):
|
|||
|
||||
def load_from_db(self):
|
||||
if not getattr(self, "_metaclass", False) and self.meta.issingle:
|
||||
self.update(frappe.db.get_singles_dict(self.doctype))
|
||||
single_doc = frappe.db.get_singles_dict(self.doctype)
|
||||
if not single_doc:
|
||||
single_doc = frappe.new_doc(self.doctype).as_dict()
|
||||
single_doc["name"] = self.doctype
|
||||
del single_doc["__islocal"]
|
||||
|
||||
self.update(single_doc)
|
||||
self.init_valid_columns()
|
||||
self._fix_numeric_types()
|
||||
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ def rename_dynamic_links(doctype, old, new):
|
|||
|
||||
# dynamic link in single, just one value to check
|
||||
if frappe.get_meta(df.parent).issingle:
|
||||
refdoc = frappe.get_singles_dict(df.parent)
|
||||
refdoc = frappe.db.get_singles_dict(df.parent)
|
||||
if refdoc.get(df.options)==doctype and refdoc.get(df.fieldname)==old:
|
||||
|
||||
frappe.db.sql("""update tabSingles set value=%s where
|
||||
|
|
|
|||
|
|
@ -54,3 +54,4 @@ execute:frappe.reload_doc('website', 'doctype', 'web_form') #2014-09-04
|
|||
execute:frappe.reload_doc('website', 'doctype', 'web_form_field') #2014-09-04
|
||||
frappe.patches.v4_2.refactor_website_routing
|
||||
frappe.patches.v4_2.set_assign_in_doc
|
||||
frappe.patches.v4_3.remove_allow_on_submit_customization
|
||||
|
|
|
|||
0
frappe/patches/v4_3/__init__.py
Normal file
0
frappe/patches/v4_3/__init__.py
Normal file
11
frappe/patches/v4_3/remove_allow_on_submit_customization.py
Normal file
11
frappe/patches/v4_3/remove_allow_on_submit_customization.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
for d in frappe.get_list("Property Setter", fields=["name", "doc_type"],
|
||||
filters={"doctype_or_field": "DocField", "property": "allow_on_submit", "value": "1"}, ignore_permissions=True):
|
||||
frappe.delete_doc("Property Setter", d.name)
|
||||
frappe.clear_cache(doctype=d.doc_type)
|
||||
|
|
@ -107,6 +107,7 @@
|
|||
"public/js/frappe/ui/tags.js",
|
||||
|
||||
"public/js/frappe/views/container.js",
|
||||
"public/js/frappe/views/factory.js",
|
||||
"public/js/frappe/views/pageview.js",
|
||||
"public/js/frappe/views/doclistview.js",
|
||||
"public/js/frappe/views/sidebar_stats.js",
|
||||
|
|
|
|||
|
|
@ -55,6 +55,11 @@ frappe.ui.form.Footer = Class.extend({
|
|||
|
||||
},
|
||||
make_tags: function() {
|
||||
if (this.frm.meta.issingle) {
|
||||
this.wrapper.find(".form-tags").toggle(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.frm.tags = new frappe.ui.TagEditor({
|
||||
parent: this.wrapper.find(".tag-area"),
|
||||
frm: this.frm,
|
||||
|
|
@ -86,7 +91,7 @@ frappe.ui.form.Footer = Class.extend({
|
|||
this.frm.attachments.refresh();
|
||||
this.frm.comments.refresh();
|
||||
this.frm.assign_to.refresh();
|
||||
this.frm.tags.refresh();
|
||||
this.frm.tags && this.frm.tags.refresh();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ frappe.ui.form.save = function(frm, action, callback, btn) {
|
|||
|
||||
if(df.reqd && !frappe.model.has_value(doc.doctype, doc.name, df.fieldname)) {
|
||||
has_errors = true;
|
||||
error_fields[error_fields.length] = df.label;
|
||||
error_fields[error_fields.length] = __(df.label);
|
||||
|
||||
// scroll to field
|
||||
if(!me.scroll_set) {
|
||||
|
|
@ -91,8 +91,8 @@ frappe.ui.form.save = function(frm, action, callback, btn) {
|
|||
});
|
||||
if(error_fields.length)
|
||||
msgprint(__('Mandatory fields required in {0}', [(doc.parenttype
|
||||
? (frappe.meta.docfield_map[doc.parenttype][doc.parentfield].label + ' (Table)')
|
||||
: doc.doctype)]) + '\n' + error_fields.join('\n'));
|
||||
? (__(frappe.meta.docfield_map[doc.parenttype][doc.parentfield].label) + ' ('+ __("Table") + ')')
|
||||
: __(doc.doctype))]) + '\n' + error_fields.join('\n'));
|
||||
});
|
||||
|
||||
return !has_errors;
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ $.extend(frappe.model, {
|
|||
doc[f.fieldname] = v;
|
||||
updated.push(f.fieldname);
|
||||
} else if(f.fieldtype == "Select" && f.options
|
||||
&& f.options!="[Select]") {
|
||||
&& !in_list(["[Select]", "Loading..."], f.options)) {
|
||||
doc[f.fieldname] = f.options.split("\n")[0];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,34 +73,3 @@ frappe.views.Container = Class.extend({
|
|||
return this.page;
|
||||
}
|
||||
});
|
||||
|
||||
frappe.views.Factory = Class.extend({
|
||||
init: function(opts) {
|
||||
$.extend(this, opts);
|
||||
},
|
||||
show: function() {
|
||||
var page_name = frappe.get_route_str(),
|
||||
me = this;
|
||||
if(frappe.pages[page_name]) {
|
||||
frappe.container.change_to(frappe.pages[page_name]);
|
||||
} else {
|
||||
var route = frappe.get_route();
|
||||
if(route[1]) {
|
||||
me.make(route);
|
||||
} else {
|
||||
frappe.show_not_found(route);
|
||||
}
|
||||
}
|
||||
},
|
||||
make_page: function(double_column) {
|
||||
var page_name = frappe.get_route_str(),
|
||||
page = frappe.container.add_page(page_name);
|
||||
|
||||
frappe.ui.make_app_page({
|
||||
parent: page,
|
||||
single_column: !double_column
|
||||
});
|
||||
frappe.container.change_to(page_name);
|
||||
return page;
|
||||
}
|
||||
})
|
||||
|
|
|
|||
36
frappe/public/js/frappe/views/factory.js
Normal file
36
frappe/public/js/frappe/views/factory.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
// MIT License. See license.txt
|
||||
|
||||
frappe.provide('frappe.pages');
|
||||
frappe.provide('frappe.views');
|
||||
|
||||
frappe.views.Factory = Class.extend({
|
||||
init: function(opts) {
|
||||
$.extend(this, opts);
|
||||
},
|
||||
show: function() {
|
||||
var page_name = frappe.get_route_str(),
|
||||
me = this;
|
||||
if(frappe.pages[page_name] && page_name.indexOf("Form/")===-1) {
|
||||
frappe.container.change_to(frappe.pages[page_name]);
|
||||
} else {
|
||||
var route = frappe.get_route();
|
||||
if(route[1]) {
|
||||
me.make(route);
|
||||
} else {
|
||||
frappe.show_not_found(route);
|
||||
}
|
||||
}
|
||||
},
|
||||
make_page: function(double_column) {
|
||||
var page_name = frappe.get_route_str(),
|
||||
page = frappe.container.add_page(page_name);
|
||||
|
||||
frappe.ui.make_app_page({
|
||||
parent: page,
|
||||
single_column: !double_column
|
||||
});
|
||||
frappe.container.change_to(page_name);
|
||||
return page;
|
||||
}
|
||||
});
|
||||
|
|
@ -44,7 +44,7 @@ frappe.views.Gantt = Class.extend({
|
|||
this.appframe.add_module_icon(module)
|
||||
this.appframe.set_views_for(this.doctype, "gantt");
|
||||
|
||||
this.appframe.set_title_right("Refresh",
|
||||
this.appframe.set_title_right(__("Refresh"),
|
||||
function() { me.refresh(); }, "icon-refresh")
|
||||
|
||||
this.appframe.add_field({fieldtype:"Date", label:"From",
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ frappe.views.QueryReport = Class.extend({
|
|||
<p class="text-muted"><br>\
|
||||
'+__('For comparative filters, start with')+' ">" or "<", e.g. >5 or >01-02-2012\
|
||||
<br>'+__('For ranges')+' ('+__('values and dates')+') use ":", \
|
||||
e.g. "5:10" (to filter values between 5 & 10)</p>\
|
||||
e.g. "5:10" (' + __("to filter values between 5 & 10") + ')</p>\
|
||||
</div>').appendTo(this.wrapper);
|
||||
|
||||
this.make_toolbar();
|
||||
|
|
@ -148,10 +148,7 @@ frappe.views.QueryReport = Class.extend({
|
|||
var data = [];
|
||||
// get filtered data
|
||||
for (var i=0, l=me.dataView.getLength(); i<l; i++) {
|
||||
var d = me.dataView.getItem(i);
|
||||
var newd = {}; data.push(newd);
|
||||
$.each(d, function(k, v) {
|
||||
newd[k.replace(/ /g, "_").toLowerCase()] = v; });
|
||||
data.push(me.dataView.getItem(i));
|
||||
}
|
||||
|
||||
var content = frappe.render(html_format,
|
||||
|
|
@ -235,7 +232,7 @@ frappe.views.QueryReport = Class.extend({
|
|||
// Run
|
||||
var me = this;
|
||||
this.waiting = frappe.messages.waiting(this.wrapper.find(".waiting-area").empty().toggle(true),
|
||||
"Loading Report...");
|
||||
__("Loading Report") + "...");
|
||||
this.wrapper.find(".results").toggle(false);
|
||||
var filters = this.get_values(true);
|
||||
|
||||
|
|
@ -326,7 +323,7 @@ frappe.views.QueryReport = Class.extend({
|
|||
var me = this;
|
||||
var formatter = this.get_formatter();
|
||||
|
||||
this.columns = [{id: "_id", field: "_id", name: "Sr No", width: 60}]
|
||||
this.columns = [{id: "_id", field: "_id", name: __("Sr No"), width: 60}]
|
||||
.concat($.map(columns, function(c) {
|
||||
if ($.isPlainObject(c)) {
|
||||
var df = c;
|
||||
|
|
@ -360,7 +357,7 @@ frappe.views.QueryReport = Class.extend({
|
|||
formatter: formatter
|
||||
});
|
||||
|
||||
col.field = df.fieldname || df.label.replace(/ /g, "_");
|
||||
col.field = df.fieldname || df.label;
|
||||
df.label = __(df.label);
|
||||
col.name = col.id = col.label = df.label;
|
||||
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ def get_messages_from_report(name):
|
|||
frappe.db.get_value("DocType", report.ref_doctype, "module"))
|
||||
if report.query:
|
||||
messages.extend(re.findall('"([^:,^"]*):', report.query))
|
||||
messages.append(report.report_name)
|
||||
messages.append(report.report_name)
|
||||
return clean(messages)
|
||||
|
||||
def get_messages_from_page_or_report(doctype, name, module=None):
|
||||
|
|
@ -240,6 +240,7 @@ def get_messages_from_page_or_report(doctype, name, module=None):
|
|||
file_path = frappe.get_module_path(module, doctype, name, name)
|
||||
messages = get_messages_from_file(file_path + ".js")
|
||||
messages += get_messages_from_file(file_path + ".html")
|
||||
messages += get_messages_from_file(file_path + ".py")
|
||||
|
||||
return clean(messages)
|
||||
|
||||
|
|
|
|||
|
|
@ -117,12 +117,12 @@ def save_file(fname, content, dt, dn, decode=False):
|
|||
content_type = mimetypes.guess_type(fname)[0]
|
||||
fname = get_file_name(fname, content_hash[-6:])
|
||||
|
||||
method = get_hook_method('write_file', fallback=save_file_on_filesystem)
|
||||
|
||||
file_data = get_file_data_from_hash(content_hash)
|
||||
if not file_data:
|
||||
method = get_hook_method('write_file', fallback=save_file_on_filesystem)
|
||||
file_data = method(fname, content, content_type=content_type)
|
||||
file_data = copy(file_data)
|
||||
|
||||
file_data.update({
|
||||
"doctype": "File Data",
|
||||
"attached_to_doctype": dt,
|
||||
|
|
@ -254,9 +254,9 @@ def get_file_name(fname, optional_suffix):
|
|||
if len(n_records) > 0 or os.path.exists(get_files_path(fname)):
|
||||
f = fname.rsplit('.', 1)
|
||||
if len(f) == 1:
|
||||
partial, extn = f[0], None
|
||||
elif len(f) == 2:
|
||||
partial, extn = f
|
||||
return '{partial}{suffix}{extn}'.format(partial=partial, extn="."+extn if extn else "", suffix=optional_suffix)
|
||||
partial, extn = f[0], ""
|
||||
else:
|
||||
partial, extn = f[0], "." + f[1]
|
||||
return '{partial}{suffix}{extn}'.format(partial=partial, extn=extn, suffix=optional_suffix)
|
||||
return fname
|
||||
|
||||
|
|
|
|||
|
|
@ -171,6 +171,7 @@ def clear_cache(path=None):
|
|||
else:
|
||||
clear_sitemap()
|
||||
frappe.clear_cache("Guest")
|
||||
frappe.cache().delete_value("_website_pages")
|
||||
clear_permissions()
|
||||
|
||||
for method in frappe.get_hooks("website_clear_cache"):
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ def run(report_name, filters=()):
|
|||
method_name = get_report_module_dotted_path(module, report.name) + ".execute"
|
||||
columns, result = frappe.get_attr(method_name)(frappe._dict(filters))
|
||||
|
||||
if report.apply_user_permissions:
|
||||
if report.apply_user_permissions and result:
|
||||
result = get_filtered_data(report.ref_doctype, columns, result)
|
||||
|
||||
if cint(report.add_total_row) and result:
|
||||
|
|
@ -138,7 +138,7 @@ def add_total_row(result, columns):
|
|||
|
||||
def get_filtered_data(ref_doctype, columns, data):
|
||||
result = []
|
||||
linked_doctypes = get_linked_doctypes(columns)
|
||||
linked_doctypes = get_linked_doctypes(columns, data)
|
||||
match_filters_per_doctype = get_user_match_filters(linked_doctypes, ref_doctype)
|
||||
|
||||
if match_filters_per_doctype:
|
||||
|
|
@ -183,7 +183,7 @@ def has_match(row, linked_doctypes, doctype_match_filters):
|
|||
|
||||
return resultant_match
|
||||
|
||||
def get_linked_doctypes(columns):
|
||||
def get_linked_doctypes(columns, data):
|
||||
linked_doctypes = {}
|
||||
|
||||
for idx, col in enumerate(columns):
|
||||
|
|
@ -197,6 +197,11 @@ def get_linked_doctypes(columns):
|
|||
elif col.get("fieldtype")=="Link" and col.get("options"):
|
||||
linked_doctypes[col["options"]] = col["fieldname"]
|
||||
|
||||
# remove doctype if column is empty
|
||||
for doctype, key in linked_doctypes.items():
|
||||
if not any(d[key] for d in data if d):
|
||||
del linked_doctypes[doctype]
|
||||
|
||||
return linked_doctypes
|
||||
|
||||
def get_user_match_filters(doctypes, ref_doctype):
|
||||
|
|
|
|||
2
setup.py
2
setup.py
|
|
@ -1,7 +1,7 @@
|
|||
from setuptools import setup, find_packages
|
||||
import os
|
||||
|
||||
version = "4.3.0"
|
||||
version = "4.4.0"
|
||||
|
||||
with open("requirements.txt", "r") as f:
|
||||
install_requires = f.readlines()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue