Browse Source

Merge branch 'develop' into perm

version-14
Suraj Shetty 3 years ago
committed by GitHub
parent
commit
3276712d84
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 234 additions and 45 deletions
  1. +57
    -0
      cypress/integration/control_autocomplete.js
  2. +2
    -2
      frappe/core/doctype/docfield/docfield.json
  3. +2
    -1
      frappe/core/doctype/report/test_report.py
  4. +2
    -2
      frappe/custom/doctype/custom_field/custom_field.json
  5. +3
    -3
      frappe/custom/doctype/customize_form_field/customize_form_field.json
  6. +2
    -1
      frappe/database/mariadb/database.py
  7. +2
    -1
      frappe/database/postgres/database.py
  8. +5
    -0
      frappe/desk/form/load.py
  9. +1
    -1
      frappe/desk/page/setup_wizard/setup_wizard.py
  10. +5
    -14
      frappe/desk/query_report.py
  11. +2
    -1
      frappe/model/__init__.py
  12. +1
    -1
      frappe/model/document.py
  13. +124
    -7
      frappe/public/js/frappe/form/controls/autocomplete.js
  14. +3
    -0
      frappe/public/js/frappe/form/formatters.js
  15. +7
    -8
      frappe/public/js/frappe/form/grid_row.js
  16. +2
    -1
      frappe/public/js/frappe/views/reports/query_report.js
  17. +12
    -1
      frappe/public/scss/common/grid.scss
  18. +2
    -1
      frappe/social/doctype/energy_point_log/energy_point_log.py

+ 57
- 0
cypress/integration/control_autocomplete.js View File

@@ -0,0 +1,57 @@
context('Control Autocomplete', () => {
before(() => {
cy.login();
cy.visit('/app/website');
});

function get_dialog_with_autocomplete(options) {
cy.visit('/app/website');
return cy.dialog({
title: 'Autocomplete',
fields: [
{
'label': 'Select an option',
'fieldname': 'autocomplete',
'fieldtype': 'Autocomplete',
'options': options || ['Option 1', 'Option 2', 'Option 3'],
}
]
});
}

it('should set the valid value', () => {
get_dialog_with_autocomplete().as('dialog');

cy.get('.frappe-control[data-fieldname=autocomplete] input').focus().as('input');
cy.wait(1000);
cy.get('@input').type('2', { delay: 300 });
cy.get('.frappe-control[data-fieldname=autocomplete]').findByRole('listbox').should('be.visible');
cy.get('.frappe-control[data-fieldname=autocomplete] input').type('{enter}', { delay: 300 });
cy.get('.frappe-control[data-fieldname=autocomplete] input').blur();
cy.get('@dialog').then(dialog => {
let value = dialog.get_value('autocomplete');
expect(value).to.eq('Option 2');
dialog.clear();
});
});

it('should set the valid value with different label', () => {
const options_with_label = [
{ label: "Option 1", value: "option_1" },
{ label: "Option 2", value: "option_2" }
];
get_dialog_with_autocomplete(options_with_label).as('dialog');

cy.get('.frappe-control[data-fieldname=autocomplete] input').focus().as('input');
cy.get('.frappe-control[data-fieldname=autocomplete]').findByRole('listbox').should('be.visible');
cy.get('@input').type('2', { delay: 300 });
cy.get('.frappe-control[data-fieldname=autocomplete] input').type('{enter}', { delay: 300 });
cy.get('.frappe-control[data-fieldname=autocomplete] input').blur();
cy.get('@dialog').then(dialog => {
let value = dialog.get_value('autocomplete');
expect(value).to.eq('option_2');
dialog.clear();
});
});

});

+ 2
- 2
frappe/core/doctype/docfield/docfield.json View File

@@ -99,7 +99,7 @@
"label": "Type",
"oldfieldname": "fieldtype",
"oldfieldtype": "Select",
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nIcon\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nRating\nSection Break\nSelect\nSignature\nSmall Text\nTab Break\nTable\nTable MultiSelect\nText\nText Editor\nTime",
"options": "Autocomplete\nAttach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nIcon\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nRating\nSection Break\nSelect\nSignature\nSmall Text\nTab Break\nTable\nTable MultiSelect\nText\nText Editor\nTime",
"reqd": 1,
"search_index": 1
},
@@ -547,7 +547,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2022-01-27 21:22:20.529072",
"modified": "2022-02-14 11:56:19.812863",
"modified_by": "Administrator",
"module": "Core",
"name": "DocField",


+ 2
- 1
frappe/core/doctype/report/test_report.py View File

@@ -330,7 +330,8 @@ result = [
}
]

result = add_total_row(result, columns, meta=None, report_settings=report_settings)
result = add_total_row(result, columns, meta=None, is_tree=report_settings['tree'],
parent_field=report_settings['parent_field'])
self.assertEqual(result[-1][0], "Total")
self.assertEqual(result[-1][1], 200)
self.assertEqual(result[-1][2], 150.50)

+ 2
- 2
frappe/custom/doctype/custom_field/custom_field.json View File

@@ -122,7 +122,7 @@
"label": "Field Type",
"oldfieldname": "fieldtype",
"oldfieldtype": "Select",
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nIcon\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nRating\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature\nTab Break",
"options": "Autocomplete\nAttach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nIcon\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nRating\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature\nTab Break",
"reqd": 1
},
{
@@ -431,7 +431,7 @@
"idx": 1,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2022-01-27 21:47:01.065556",
"modified": "2022-02-14 15:42:21.885999",
"modified_by": "Administrator",
"module": "Custom",
"name": "Custom Field",


+ 3
- 3
frappe/custom/doctype/customize_form_field/customize_form_field.json View File

@@ -85,7 +85,7 @@
"label": "Type",
"oldfieldname": "fieldtype",
"oldfieldtype": "Select",
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nIcon\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSignature\nSmall Text\nTab Break\nTable\nTable MultiSelect\nText\nText Editor\nTime",
"options": "Autocomplete\nAttach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nIcon\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSignature\nSmall Text\nTab Break\nTable\nTable MultiSelect\nText\nText Editor\nTime",
"reqd": 1,
"search_index": 1
},
@@ -450,7 +450,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2022-02-08 19:38:16.111199",
"modified": "2022-02-25 16:01:12.616736",
"modified_by": "Administrator",
"module": "Custom",
"name": "Customize Form Field",
@@ -460,4 +460,4 @@
"sort_field": "modified",
"sort_order": "ASC",
"states": []
}
}

+ 2
- 1
frappe/database/mariadb/database.py View File

@@ -52,7 +52,8 @@ class MariaDBDatabase(Database):
'Barcode': ('longtext', ''),
'Geolocation': ('longtext', ''),
'Duration': ('decimal', '21,9'),
'Icon': ('varchar', self.VARCHAR_LEN)
'Icon': ('varchar', self.VARCHAR_LEN),
'Autocomplete': ('varchar', self.VARCHAR_LEN),
}

def get_connection(self):


+ 2
- 1
frappe/database/postgres/database.py View File

@@ -62,7 +62,8 @@ class PostgresDatabase(Database):
'Barcode': ('text', ''),
'Geolocation': ('text', ''),
'Duration': ('decimal', '21,9'),
'Icon': ('varchar', self.VARCHAR_LEN)
'Icon': ('varchar', self.VARCHAR_LEN),
'Autocomplete': ('varchar', self.VARCHAR_LEN),
}

def get_connection(self):


+ 5
- 0
frappe/desk/form/load.py View File

@@ -11,8 +11,10 @@ from frappe.model.utils.user_settings import get_user_settings
from frappe.permissions import get_doc_permissions
from frappe.desk.form.document_follow import is_document_followed
from frappe import _
from frappe import _dict
from urllib.parse import quote


@frappe.whitelist()
def getdoc(doctype, name, user=None):
"""
@@ -50,8 +52,11 @@ def getdoc(doctype, name, user=None):

doc.add_seen()
set_link_titles(doc)
if frappe.response.docs is None:
frappe.response = _dict({"docs": []})
frappe.response.docs.append(doc)


@frappe.whitelist()
def getdoctype(doctype, with_parent=False, cached_timestamp=None):
"""load doctype"""


+ 1
- 1
frappe/desk/page/setup_wizard/setup_wizard.py View File

@@ -392,7 +392,7 @@ def make_records(records, debug=False):
doc.flags.ignore_mandatory = True

try:
doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
doc.insert(ignore_permissions=True)
frappe.db.commit()

except frappe.DuplicateEntryError as e:


+ 5
- 14
frappe/desk/query_report.py View File

@@ -73,7 +73,7 @@ def get_report_result(report, filters):
return res

@frappe.read_only()
def generate_report_result(report, filters=None, user=None, custom_columns=None, report_settings=None):
def generate_report_result(report, filters=None, user=None, custom_columns=None, is_tree=False, parent_field=None):
user = user or frappe.session.user
filters = filters or []

@@ -108,7 +108,7 @@ def generate_report_result(report, filters=None, user=None, custom_columns=None,
result = get_filtered_data(report.ref_doctype, columns, result, user)

if cint(report.add_total_row) and result and not skip_total_row:
result = add_total_row(result, columns, report_settings=report_settings)
result = add_total_row(result, columns, is_tree=is_tree, parent_field=parent_field)

return {
"result": result,
@@ -210,7 +210,7 @@ def get_script(report_name):

@frappe.whitelist()
@frappe.read_only()
def run(report_name, filters=None, user=None, ignore_prepared_report=False, custom_columns=None, report_settings=None):
def run(report_name, filters=None, user=None, ignore_prepared_report=False, custom_columns=None, is_tree=False, parent_field=None):
report = get_report_doc(report_name)
if not user:
user = frappe.session.user
@@ -238,7 +238,7 @@ def run(report_name, filters=None, user=None, ignore_prepared_report=False, cust
dn = ""
result = get_prepared_report_result(report, filters, dn, user)
else:
result = generate_report_result(report, filters, user, custom_columns, report_settings)
result = generate_report_result(report, filters, user, custom_columns, is_tree, parent_field)

result["add_total_row"] = report.add_total_row and not result.get(
"skip_total_row", False
@@ -435,18 +435,9 @@ def build_xlsx_data(columns, data, visible_idx, include_indentation, ignore_visi
return result, column_widths


def add_total_row(result, columns, meta=None, report_settings=None):
def add_total_row(result, columns, meta=None, is_tree=False, parent_field=None):
total_row = [""] * len(columns)
has_percent = []
is_tree = False
parent_field = ''

if report_settings:
if isinstance(report_settings, (str,)):
report_settings = json.loads(report_settings)

is_tree = report_settings.get('tree')
parent_field = report_settings.get('parent_field')

for i, col in enumerate(columns):
fieldtype, options, fieldname = None, None, None


+ 2
- 1
frappe/model/__init__.py View File

@@ -35,7 +35,8 @@ data_fieldtypes = (
'Barcode',
'Geolocation',
'Duration',
'Icon'
'Icon',
'Autocomplete',
)

attachment_fieldtypes = (


+ 1
- 1
frappe/model/document.py View File

@@ -1154,7 +1154,7 @@ class Document(BaseDocument):
for f in hooks:
add_to_return_value(self, f(self, method, *args, **kwargs))

return self._return_value
return self.__dict__.pop("_return_value", None)

return runner



+ 124
- 7
frappe/public/js/frappe/form/controls/autocomplete.js View File

@@ -11,7 +11,26 @@ frappe.ui.form.ControlAutocomplete = class ControlAutoComplete extends frappe.ui
set_options() {
if (this.df.options) {
let options = this.df.options || [];
this._data = this.parse_options(options);
this.set_data(options);
}
}

format_for_input(value) {
if (value == null) {
return "";
} else if (this._data && this._data.length) {
const item = this._data.find(i => i.value == value);
return item ? item.label : value;
} else {
return value;
}
}

get_input_value() {
if (this.$input) {
const label = this.$input.val();
const item = this._data?.find(i => i.label == label);
return item ? item.value : label;
}
}

@@ -23,7 +42,7 @@ frappe.ui.form.ControlAutocomplete = class ControlAutoComplete extends frappe.ui
autoFirst: true,
list: this.get_data(),
data: function(item) {
if (!(item instanceof Object)) {
if (typeof item !== 'object') {
var d = { value: item };
item = d;
}
@@ -65,6 +84,18 @@ frappe.ui.form.ControlAutocomplete = class ControlAutoComplete extends frappe.ui
};
}

init_option_cache() {
if (!this.$input.cache) {
this.$input.cache = {};
}
if (!this.$input.cache[this.doctype]) {
this.$input.cache[this.doctype] = {};
}
if (!this.$input.cache[this.doctype][this.df.fieldname]) {
this.$input.cache[this.doctype][this.df.fieldname] = {};
}
}

setup_awesomplete() {
this.awesomplete = new Awesomplete(
this.input,
@@ -75,12 +106,18 @@ frappe.ui.form.ControlAutocomplete = class ControlAutoComplete extends frappe.ui
.find('.awesomplete ul')
.css('min-width', '100%');

this.$input.on(
'input',
frappe.utils.debounce(() => {
this.init_option_cache();

this.$input.on('input', frappe.utils.debounce((e) => {
const cached_options = this.$input.cache[this.doctype][this.df.fieldname][e.target.value];
if (cached_options && cached_options.length) {
this.set_data(cached_options);
} else if (this.get_query || this.df.get_query) {
this.execute_query_if_exists(e.target.value);
} else {
this.awesomplete.list = this.get_data();
}, 500)
);
}
}, 500));

this.$input.on('focus', () => {
if (!this.$input.val()) {
@@ -89,6 +126,17 @@ frappe.ui.form.ControlAutocomplete = class ControlAutoComplete extends frappe.ui
}
});

this.$input.on("blur", () => {
if(this.selected) {
this.selected = false;
return;
}
var value = this.get_input_value();
if(value!==this.last_value) {
this.parse_validate_and_set_in_model(value);
}
});

this.$input.on("awesomplete-open", () => {
this.autocomplete_open = true;
});
@@ -127,6 +175,75 @@ frappe.ui.form.ControlAutocomplete = class ControlAutoComplete extends frappe.ui
return options;
}

execute_query_if_exists(term) {
const args = { txt: term };
let get_query = this.get_query || this.df.get_query;

if (!get_query) {
return;
}

let set_nulls = function(obj) {
$.each(obj, function(key, value) {
if (value !== undefined) {
obj[key] = value;
}
});
return obj;
};

let process_query_object = function(obj) {
if (obj.query) {
args.query = obj.query;
}

if (obj.params) {
set_nulls(obj.params);
Object.assign(args, obj.params);
}

// turn off value translation
if (obj.translate_values !== undefined) {
this.translate_values = obj.translate_values;
}
};

if ($.isPlainObject(get_query)) {
process_query_object(get_query);
} else if (typeof get_query === "string") {
args.query = get_query;
} else {
// get_query by function
var q = get_query(
(this.frm && this.frm.doc) || this.doc,
this.doctype,
this.docname
);

if (typeof q === "string") {
// returns a string
args.query = q;
} else if ($.isPlainObject(q)) {
// returns an object
process_query_object(q);
}
}

if (args.query) {
frappe.call({
method: args.query,
args: args,
callback: ({ message }) => {
if(!this.$input.is(":focus")) {
return;
}
this.$input.cache[this.doctype][this.df.fieldname][term] = message;
this.set_data(message);
}
})
}
}

get_data() {
return this._data || [];
}


+ 3
- 0
frappe/public/js/frappe/form/formatters.js View File

@@ -21,6 +21,9 @@ frappe.form.formatters = {
}
return value==null ? "" : value;
},
Autocomplete: function(value) {
return __(frappe.form.formatters["Data"](value));
},
Select: function(value) {
return __(frappe.form.formatters["Data"](value));
},


+ 7
- 8
frappe/public/js/frappe/form/grid_row.js View File

@@ -183,21 +183,20 @@ export default class GridRow {
render_template() {
this.set_row_index();

if(this.row_display) {
if (this.row_display) {
this.row_display.remove();
}

// row index
if(this.doc) {
if(!this.row_index) {
this.row_index = $('<div style="float: left; margin-left: 15px; margin-top: 8px; \
margin-right: -20px;">'+this.row_check_html+' <span></span></div>').appendTo(this.row);
}
if (!this.row_index) {
this.row_index = $(`<div class="template-row-index">${this.row_check_html}<span></span></div>`).appendTo(this.row);
}
if (this.doc) {
this.row_index.find('span').html(this.doc.idx);
}

this.row_display = $('<div class="row-data sortable-handle template-row">'+
+'</div>').appendTo(this.row)
this.row_display = $('<div class="row-data sortable-handle template-row"></div>').appendTo(this.row)
.html(frappe.render(this.grid.template, {
doc: this.doc ? frappe.get_format_helper(this.doc) : null,
frm: this.frm,


+ 2
- 1
frappe/public/js/frappe/views/reports/query_report.js View File

@@ -578,7 +578,8 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
args: {
report_name: this.report_name,
filters: filters,
report_settings: this.report_settings
is_tree: this.report_settings.tree,
parent_field: this.report_settings.parent_field
},
callback: resolve,
always: () => this.page.btn_secondary.prop('disabled', false)


+ 12
- 1
frappe/public/scss/common/grid.scss View File

@@ -54,7 +54,7 @@
}

.form-grid .grid-heading-row .template-row {
margin-left: 20px;
margin-left: 8px;
}

.form-grid .template-row {
@@ -88,6 +88,17 @@
margin-top: 2px;
}

.template-row-index {
float: left;
margin-left: 15px;
margin-top: 8px;
margin-right: -20px;

span {
margin-left: 5px;
}
}

.editable-form .grid-static-col.bold {
font-weight: bold;
}


+ 2
- 1
frappe/social/doctype/energy_point_log/energy_point_log.py View File

@@ -164,6 +164,7 @@ def get_alert_dict(doc):

return alert_dict


def create_energy_points_log(ref_doctype, ref_name, doc, apply_only_once=False):
doc = frappe._dict(doc)

@@ -171,7 +172,7 @@ def create_energy_points_log(ref_doctype, ref_name, doc, apply_only_once=False):
ref_name, doc.rule, None if apply_only_once else doc.user)

if log_exists:
return
return frappe.get_doc('Energy Point Log', log_exists)

new_log = frappe.new_doc('Energy Point Log')
new_log.reference_doctype = ref_doctype


Loading…
Cancel
Save