Merge branch 'develop' of https://github.com/frappe/frappe into pref_global_search

This commit is contained in:
Himanshu Warekar 2019-12-04 23:13:27 +05:30
commit f616a7c814
15 changed files with 722 additions and 891 deletions

View file

@ -0,0 +1,50 @@
context('Grid Pagination', () => {
beforeEach(() => {
cy.login();
cy.visit('/desk');
});
before(() => {
cy.login();
cy.visit('/desk');
cy.window().its('frappe').then(frappe => {
frappe.call("frappe.tests.ui_test_helpers.create_contact_phone_nos_records");
});
});
it('creates pages for child table', () => {
cy.visit('/desk#Form/Contact/Test Contact');
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table');
cy.get('@table').find('.current-page-number').should('contain', '1');
cy.get('@table').find('.total-page-number').should('contain', '50');
cy.get('@table').find('.grid-body .grid-row').should('have.length', 20);
});
it('goes to the next and previous page', () => {
cy.visit('/desk#Form/Contact/Test Contact');
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table');
cy.get('@table').find('.next-page').click();
cy.get('@table').find('.current-page-number').should('contain', '2');
cy.get('@table').find('.grid-body .grid-row').first().should('have.attr', 'data-idx', '21');
cy.get('@table').find('.prev-page').click();
cy.get('@table').find('.current-page-number').should('contain', '1');
cy.get('@table').find('.grid-body .grid-row').first().should('have.attr', 'data-idx', '1');
});
it('adds and deletes rows and changes page', ()=> {
cy.visit('/desk#Form/Contact/Test Contact');
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table');
cy.get('@table').find('button.grid-add-row').click();
cy.get('@table').find('.grid-body .row-index').should('contain', 1001);
cy.get('@table').find('.current-page-number').should('contain', '51');
cy.get('@table').find('.total-page-number').should('contain', '51');
cy.get('@table').find('.grid-body .grid-row .grid-row-check').click({force: true});
cy.get('@table').find('button.grid-remove-rows').click();
cy.get('@table').find('.grid-body .row-index').last().should('contain', 1000);
cy.get('@table').find('.current-page-number').should('contain', '50');
cy.get('@table').find('.total-page-number').should('contain', '50');
});
it('deletes all rows', ()=> {
cy.visit('/desk#Form/Contact/Test Contact');
cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table');
cy.get('@table').find('.grid-heading-row .grid-row-check').click({force: true});
cy.get('@table').find('button.grid-remove-all-rows').click();
cy.get('@table').find('.grid-body .grid-row').should('have.length', 0);
});
});

View file

@ -36,13 +36,15 @@ def generate_and_cache_results(chart, chart_name, function, cache_key):
def get_from_date_from_timespan(to_date, timespan):
days = months = years = 0
if "Last Week" == timespan:
if timespan == "Last Week":
days = -7
if "Last Month" == timespan:
if timespan == "Last Month":
months = -1
elif "Last Quarter" == timespan:
elif timespan == "Last Quarter":
months = -3
elif "Last Year" == timespan:
elif timespan == "Last Year":
years = -1
elif timespan == "All Time":
years = -50
return add_to_date(to_date, years=years, months=months, days=days,
as_datetime=True)

View file

@ -3,6 +3,15 @@
frappe.ui.form.on('Bulk Update', {
refresh: function(frm) {
frm.set_query("document_type", function() {
return {
filters: [
['DocType', 'issingle', '=', 0],
['DocType', 'name', 'not in', frappe.model.core_doctypes_list]
]
};
});
frm.page.set_primary_action(__('Update'), function() {
if (!frm.doc.update_value) {
frappe.throw(__('Field "value" is mandatory. Please specify value to be updated'));

View file

@ -41,6 +41,7 @@ frappe.ui.form.on('Dashboard Chart', {
timespan: function(frm) {
const time_interval_options = {
"Select Date Range": ["Quarterly", "Monthly", "Weekly", "Daily"],
"All Time": ["Yearly", "Monthly"],
"Last Year": ["Quarterly", "Monthly", "Weekly", "Daily"],
"Last Quarter": ["Monthly", "Weekly", "Daily"],
"Last Month": ["Weekly", "Daily"],

View file

@ -82,14 +82,14 @@
"fieldname": "timespan",
"fieldtype": "Select",
"label": "Timespan",
"options": "Last Year\nLast Quarter\nLast Month\nLast Week\nSelect Date Range"
"options": "All Time\nLast Year\nLast Quarter\nLast Month\nLast Week\nSelect Date Range"
},
{
"depends_on": "timeseries",
"fieldname": "time_interval",
"fieldtype": "Select",
"label": "Time Interval",
"options": "Quarterly\nMonthly\nWeekly\nDaily"
"options": "Yearly\nQuarterly\nMonthly\nWeekly\nDaily"
},
{
"default": "0",
@ -187,7 +187,7 @@
"label": "To Date"
}
],
"modified": "2019-11-04 12:32:14.525409",
"modified": "2019-11-18 16:20:11.529496",
"modified_by": "Administrator",
"module": "Desk",
"name": "Dashboard Chart",

View file

@ -74,8 +74,7 @@ def get_chart_config(chart, filters, timespan, timegrain, from_date, to_date):
result = convert_to_dates(data, timegrain)
# add missing data points for periods where there was no result
result = add_missing_values(result, timegrain, from_date, to_date)
result = add_missing_values(result, timegrain, timespan, from_date, to_date)
chart_config = {
"labels": [formatdate(r[0].strftime('%Y-%m-%d')) for r in result],
"datasets": [{
@ -133,7 +132,9 @@ def get_aggregate_function(chart_type):
"Average": "AVG",
}[chart_type]
def convert_to_dates(data, timegrain):
""" Converts individual dates within data to the end of period """
result = []
for d in data:
if timegrain == 'Daily':
@ -141,10 +142,11 @@ def convert_to_dates(data, timegrain):
elif timegrain == 'Weekly':
result.append([add_to_date(add_to_date('{:d}-01-01'.format(int(d[0])), weeks = d[1] + 1), days = -1), d[2]])
elif timegrain == 'Monthly':
result.append([add_to_date(add_to_date('{:d}-01-01'.format(int(d[0])), months = d[1]), days = -1), d[2]])
result.append([add_to_date(add_to_date('{:d}-01-01'.format(int(d[0])), months=d[1]), days = -1), d[2]])
elif timegrain == 'Quarterly':
result.append([add_to_date(add_to_date('{:d}-01-01'.format(int(d[0])), months = d[1] * 3), days = -1), d[2]])
result.append([add_to_date(add_to_date('{:d}-01-01'.format(int(d[0])), months=d[1] * 3), days = -1), d[2]])
elif timegrain == 'Yearly':
result.append([add_to_date(add_to_date('{:d}-01-01'.format(int(d[0])), months=12), days = -1), d[2]])
result[-1][0] = getdate(result[-1][0])
return result
@ -164,17 +166,17 @@ def get_unit_function(datefield, timegrain):
return unit_function
def add_missing_values(data, timegrain, from_date, to_date):
def add_missing_values(data, timegrain, timespan, from_date, to_date):
# add missing intervals
result = []
first_expected_date = get_period_ending(from_date, timegrain)
# fill out data before the first data point
first_data_point_date = data[0][0] if data else getdate(add_to_date(to_date, days=1))
while first_data_point_date > first_expected_date:
result.append([first_expected_date, 0.0])
first_expected_date = get_next_expected_date(first_expected_date, timegrain)
if timespan != 'All Time':
first_expected_date = get_period_ending(from_date, timegrain)
# fill out data before the first data point
first_data_point_date = data[0][0] if data else getdate(add_to_date(to_date, days=1))
while first_data_point_date > first_expected_date:
result.append([first_expected_date, 0.0])
first_expected_date = get_next_expected_date(first_expected_date, timegrain)
# fill data points and missing points
for i, d in enumerate(data):
@ -212,14 +214,16 @@ def get_next_expected_date(date, timegrain):
def get_period_ending(date, timegrain):
date = getdate(date)
if timegrain=='Daily':
if timegrain == 'Daily':
pass
elif timegrain=='Weekly':
elif timegrain == 'Weekly':
date = get_week_ending(date)
elif timegrain=='Monthly':
elif timegrain == 'Monthly':
date = get_month_ending(date)
elif timegrain=='Quarterly':
elif timegrain == 'Quarterly':
date = get_quarter_ending(date)
elif timegrain == 'Yearly':
date = get_year_ending(date)
return getdate(date)
@ -231,7 +235,7 @@ def get_week_ending(date):
# first day of next week
date = add_to_date('{}-01-01'.format(date.year), weeks = week_of_the_year + 1)
# last day of this week
return add_to_date(date, days = -1)
return add_to_date(date, days=-1)
def get_month_ending(date):
month_of_the_year = int(date.strftime('%m'))
@ -239,7 +243,7 @@ def get_month_ending(date):
date = add_to_date('{}-01-01'.format(date.year), months = month_of_the_year)
# last day of this month
return add_to_date(date, days = -1)
return add_to_date(date, days=-1)
def get_quarter_ending(date):
date = getdate(date)
@ -255,8 +259,17 @@ def get_quarter_ending(date):
return date
def get_year_ending(date):
''' returns year ending of the given date '''
# first day of next year (note year starts from 1)
date = add_to_date('{}-01-01'.format(date.year), months = 12)
# last day of this month
return add_to_date(date, days=-1)
class DashboardChart(Document):
def on_update(self):
frappe.cache().delete_key('chart-data:{}'.format(self.name))

View file

@ -36,6 +36,9 @@ class TestDashboardChart(unittest.TestCase):
self.assertEqual(get_period_ending('2019-10-01', 'Quarterly'),
getdate('2019-12-31'))
self.assertEqual(get_period_ending('2019-10-01', 'Yearly'),
getdate('2019-12-31'))
def test_dashboard_chart(self):
if frappe.db.exists('Dashboard Chart', 'Test Dashboard Chart'):
frappe.delete_doc('Dashboard Chart', 'Test Dashboard Chart')

View file

@ -1,700 +1,183 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"beta": 0,
"creation": "2017-11-18 15:36:09.676722",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 1,
"engine": "InnoDB",
"allow_import": 1,
"creation": "2017-11-18 15:36:09.676722",
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"enable_social_login",
"client_credentials",
"social_login_provider",
"client_id",
"column_break_0",
"provider_name",
"client_secret",
"sb_identity_details",
"icon",
"column_break_1",
"base_url",
"client_urls",
"authorize_url",
"access_token_url",
"column_break_3",
"redirect_url",
"api_endpoint",
"custom_base_url",
"client_information",
"api_endpoint_args",
"auth_url_data",
"user_id_property"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "enable_social_login",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Enable Social Login",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"default": "0",
"fieldname": "enable_social_login",
"fieldtype": "Check",
"label": "Enable Social Login"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "eval:doc.enable_social_login",
"columns": 0,
"fieldname": "client_credentials",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Client Credentials",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"collapsible": 1,
"collapsible_depends_on": "eval:doc.enable_social_login",
"fieldname": "client_credentials",
"fieldtype": "Section Break",
"label": "Client Credentials"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Custom",
"depends_on": "eval:doc.custom!=1",
"fieldname": "social_login_provider",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Social Login Provider",
"length": 0,
"no_copy": 0,
"options": "Custom\nFacebook\nFrappe\nGitHub\nGoogle\nOffice 365\nSalesforce\nfairlogin",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 1,
"unique": 0
},
"default": "Custom",
"depends_on": "eval:doc.custom!=1",
"fieldname": "social_login_provider",
"fieldtype": "Select",
"label": "Social Login Provider",
"options": "Custom\nFacebook\nFrappe\nGitHub\nGoogle\nOffice 365\nSalesforce\nfairlogin",
"set_only_once": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "client_id",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Client ID",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "client_id",
"fieldtype": "Data",
"label": "Client ID"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_0",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "column_break_0",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "provider_name",
"fieldtype": "Data",
"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": "Provider Name",
"length": 0,
"no_copy": 0,
"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": 1,
"unique": 0
},
"fieldname": "provider_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Provider Name",
"reqd": 1,
"set_only_once": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "client_secret",
"fieldtype": "Password",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Client Secret",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "client_secret",
"fieldtype": "Password",
"label": "Client Secret"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "eval:doc.custom_base_url",
"columns": 0,
"depends_on": "",
"fieldname": "sb_identity_details",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Identity Details",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"collapsible": 1,
"collapsible_depends_on": "eval:doc.custom_base_url",
"fieldname": "sb_identity_details",
"fieldtype": "Section Break",
"label": "Identity Details"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "icon",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Icon",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "icon",
"fieldtype": "Data",
"label": "Icon"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_1",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "column_break_1",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "base_url",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Base URL",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "base_url",
"fieldtype": "Data",
"label": "Base URL"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "eval:doc.social_login_provider===\"Custom\"",
"columns": 0,
"depends_on": "",
"fieldname": "client_urls",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Client URLs",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"collapsible": 1,
"collapsible_depends_on": "eval:doc.social_login_provider===\"Custom\"",
"fieldname": "client_urls",
"fieldtype": "Section Break",
"label": "Client URLs"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "authorize_url",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Authorize URL",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "authorize_url",
"fieldtype": "Data",
"label": "Authorize URL"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "access_token_url",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Access Token URL",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "access_token_url",
"fieldtype": "Data",
"label": "Access Token URL"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "column_break_3",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "redirect_url",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Redirect URL",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "redirect_url",
"fieldtype": "Data",
"label": "Redirect URL"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "api_endpoint",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "API Endpoint",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "api_endpoint",
"fieldtype": "Data",
"label": "API Endpoint"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "custom_base_url",
"fieldtype": "Check",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Custom Base URL",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"default": "0",
"fieldname": "custom_base_url",
"fieldtype": "Check",
"hidden": 1,
"label": "Custom Base URL"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "eval:doc.social_login_provider===\"Custom\"",
"columns": 0,
"depends_on": "",
"fieldname": "client_information",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Client Information",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"collapsible": 1,
"collapsible_depends_on": "eval:doc.social_login_provider===\"Custom\"",
"fieldname": "client_information",
"fieldtype": "Section Break",
"label": "Client Information"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "api_endpoint_args",
"fieldtype": "Code",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "API Endpoint Args",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
"fieldname": "api_endpoint_args",
"fieldtype": "Code",
"label": "API Endpoint Args"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "auth_url_data",
"fieldtype": "Code",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Auth URL Data",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
"fieldname": "auth_url_data",
"fieldtype": "Code",
"label": "Auth URL Data"
},
{
"depends_on": "eval:doc.social_login_provider===\"Custom\"",
"fieldname": "user_id_property",
"fieldtype": "Data",
"label": "User ID Property"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-09-15 09:00:00.000000",
"modified_by": "Administrator",
"module": "Integrations",
"name": "Social Login Key",
"name_case": "",
"owner": "Administrator",
],
"modified": "2019-12-03 12:35:55.115260",
"modified_by": "Administrator",
"module": "Integrations",
"name": "Social Login Key",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 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
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "provider_name",
"track_changes": 1,
"track_seen": 0
}
],
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "provider_name",
"track_changes": 1
}

View file

@ -80,8 +80,8 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa
if not (for_reload or frappe.flags.in_migrate or frappe.flags.in_install or frappe.flags.in_test):
try:
delete_controllers(name, doc.module)
except (FileNotFoundError, OSError):
# in case a doctype doesnt have any controller code
except (FileNotFoundError, OSError, KeyError):
# in case a doctype doesnt have any controller code nor any app and module
pass
else:

View file

@ -352,7 +352,11 @@ frappe.ui.form.Form = class FrappeForm {
}
}
// reset visible columns, since column headings can change in different docs
this.grids.forEach(grid_obj => grid_obj.grid.visible_columns = null);
this.grids.forEach(grid_obj => {
grid_obj.grid.visible_columns = null
// reset page number to 1
grid_obj.grid.grid_pagination.go_to_page(1);
});
frappe.ui.form.close_grid_form();
this.docname = docname;
}

View file

@ -2,6 +2,7 @@
// MIT License. See license.txt
import GridRow from "./grid_row";
import GridPagination from './grid_pagination';
frappe.ui.form.get_open_grid_form = function() {
return $(".grid-row-open").data("grid_row");
@ -47,8 +48,8 @@ export default class Grid {
return false;
}
}
make() {
var me = this;
let template = `<div class="form-group">
<div class="clearfix">
@ -63,25 +64,38 @@ export default class Grid {
</div>
<div class="small form-clickable-section grid-footer">
<div class="row">
<div class="col-sm-6 grid-buttons">
<div class="col-sm-5 grid-buttons">
<button type="reset"
class="btn btn-xs btn-danger grid-remove-rows hidden"
style="margin-right: 4px;">
${__("Delete")}</button>
style="margin-right: 4px;"
data-action="delete_rows">
${__("Delete")}
</button>
<button type="reset"
class="btn btn-xs btn-danger grid-remove-all-rows hidden"
style="margin-right: 4px;"
data-action="delete_all_rows">
${__("Delete All")}
</button>
<button type="reset"
class="grid-add-multiple-rows btn btn-xs btn-default hidden"
style="margin-right: 4px;">
${__("Add Multiple")}</a>
</button>
<!-- hack to allow firefox include this in tabs -->
<button type="reset" class="btn btn-xs btn-default grid-add-row">
${__("Add Row")}</button>
<button type="reset"
class="btn btn-xs btn-default grid-add-row">
${__("Add Row")}
</button>
</div>
<div class="col-sm-6 text-right">
<div class="col-sm-4 grid-pagination">
</div>
<div class="col-sm-3 text-right">
<a href="#" class="grid-download btn btn-xs btn-default hidden"
style="margin-left: 10px;">
style="margin-left: 4px;">
${__("Download")}</a>
<a href="#" class="grid-upload btn btn-xs btn-default hidden"
style="margin-left: 10px;">
style="margin-left: 4px;">
${__("Upload")}</a>
</div>
</div>
@ -90,20 +104,20 @@ export default class Grid {
this.wrapper = $(template)
.appendTo(this.parent)
.attr("data-fieldname", this.df.fieldname);
.attr('data-fieldname', this.df.fieldname);
frappe.utils.bind_actions_with_object(this.wrapper, this);
this.form_grid = this.wrapper.find('.form-grid');
this.wrapper.find(".grid-add-row").click(function() {
me.add_new_row(null, null, true);
me.set_focus_on_row();
this.setup_add_row();
return false;
});
this.setup_grid_pagination();
this.custom_buttons = {};
this.grid_buttons = this.wrapper.find('.grid-buttons');
this.remove_rows_button = this.grid_buttons.find('.grid-remove-rows')
this.remove_rows_button = this.grid_buttons.find('.grid-remove-rows');
this.remove_all_rows_button = this.grid_buttons.find('.grid-remove-all-rows');
this.setup_allow_bulk_edit();
this.setup_check();
@ -112,79 +126,118 @@ export default class Grid {
}
}
setup_check() {
var me = this;
setup_grid_pagination() {
this.grid_pagination = new GridPagination({
grid: this,
wrapper: this.wrapper,
});
}
this.wrapper.on('click', '.grid-row-check', function(e) {
var $check = $(this);
if($check.parents('.grid-heading-row:first').length!==0) {
setup_check() {
this.wrapper.on('click', '.grid-row-check', (e) => {
var $check = $(e.currentTarget);
if ($check.parents('.grid-heading-row:first').length !== 0) {
// select all?
var checked = $check.prop('checked');
$check.parents('.form-grid:first')
.find('.grid-row-check').prop('checked', checked);
// set all
(me.grid_rows || []).forEach(function(row) { row.doc.__checked = checked ? 1 : 0; });
let result_length = this.grid_pagination.get_result_length();
let page_index = this.grid_pagination.page_index;
let page_length = this.grid_pagination.page_length;
for (var ri = (page_index-1)*page_length; ri < result_length; ri++) {
this.grid_rows[ri].doc.__checked = checked ? 1: 0;
}
} else {
var docname = $check.parents('.grid-row:first').attr('data-name');
me.grid_rows_by_docname[docname].select($check.prop('checked'));
this.grid_rows_by_docname[docname].select($check.prop('checked'));
}
me.refresh_remove_rows_button();
this.refresh_remove_rows_button();
});
this.remove_rows_button.on('click', function() {
var dirty = false;
let tasks = [];
me.get_selected_children().forEach((doc) => {
tasks.push(() => {
if (!me.frm) {
me.df.data = me.get_data();
me.df.data = me.df.data.filter((row)=> row.idx != doc.idx);
}
me.grid_rows_by_docname[doc.name].remove();
dirty = true;
});
tasks.push(() => frappe.timeout(0.1));
});
if (!me.frm) {
tasks.push(() => {
// reorder idx of df.data
me.df.data.forEach((row, index) => row.idx = index + 1);
});
}
tasks.push(() => {
if (dirty) me.refresh();
});
frappe.run_serially(tasks);
});
}
delete_rows() {
var dirty = false;
let tasks = [];
let selected_children = this.get_selected_children();
selected_children.forEach(doc => {
tasks.push(() => {
if (!this.frm) {
this.df.data = this.get_data();
this.df.data = this.df.data.filter(row => row.idx != doc.idx);
}
this.grid_rows_by_docname[doc.name].remove();
dirty = true;
});
tasks.push(() => frappe.timeout(0.1));
});
if (!this.frm) {
tasks.push(() => {
// reorder idx of df.data
this.df.data.forEach((row, index) => row.idx = index + 1);
});
}
tasks.push(() => {
if (dirty) this.refresh();
});
frappe.run_serially(tasks);
if (selected_children.length == this.grid_pagination.page_length) {
frappe.utils.scroll_to(this.wrapper);
}
}
delete_all_rows() {
this.frm.doc[this.df.fieldname] = [];
$(this.parent).find('.rows').empty();
this.grid_rows = [];
this.refresh();
frappe.utils.scroll_to(this.wrapper);
}
select_row(name) {
this.grid_rows_by_docname[name].select();
}
remove_all() {
this.grid_rows.forEach(row => {
row.remove();
});
}
refresh_remove_rows_button() {
this.remove_rows_button.toggleClass('hidden',
this.wrapper.find('.grid-body .grid-row-check:checked:first').length ? false : true);
this.remove_all_rows_button.toggleClass('hidden',
this.wrapper.find('.grid-body .grid-row-check:checked:first').length ? false : true);
}
get_selected() {
return (this.grid_rows || []).map(function(row) { return row.doc.__checked ? row.doc.name : null; })
.filter(function(d) { return d; });
return (this.grid_rows || []).map(row => {
return row.doc.__checked ? row.doc.name : null;
}).filter(d => {
return d;
});
}
get_selected_children() {
return (this.grid_rows || []).map(function(row) { return row.doc.__checked ? row.doc : null; })
.filter(function(d) { return d; });
return (this.grid_rows || []).map(row => {
return row.doc.__checked ? row.doc : null;
}).filter(d => {
return d;
});
}
make_head() {
// labels
if(!this.header_row) {
if (!this.header_row) {
this.header_row = new GridRow({
parent: $(this.parent).find(".grid-heading-row"),
parent_df: this.df,
@ -194,15 +247,16 @@ export default class Grid {
});
}
}
refresh(force) {
this.data = this.get_data();
!this.wrapper && this.make();
var me = this,
$rows = $(me.parent).find(".rows"),
data = this.get_data();
let $rows = $(this.parent).find('.rows');
this.setup_fields();
if(this.frm) {
if (this.frm) {
this.display_status = frappe.perm.get_field_display_status(this.df, this.frm.doc,
this.perm);
} else {
@ -213,24 +267,57 @@ export default class Grid {
if(this.display_status === "None") return;
// redraw
var _scroll_y = $(document).scrollTop();
this.make_head();
if(!this.grid_rows) {
if (!this.grid_rows) {
this.grid_rows = [];
}
this.truncate_rows(data);
this.truncate_rows();
this.grid_rows_by_docname = {};
for(var ri=0; ri < data.length; ri++) {
var d = data[ri];
this.grid_pagination.update_page_numbers();
this.render_result_rows($rows, false);
this.grid_pagination.check_page_number();
this.wrapper.find('.grid-empty').toggleClass('hidden', Boolean(this.data.length));
if(d.idx===undefined) {
// toolbar
this.setup_toolbar();
this.toggle_checkboxes(this.display_status !== 'Read');
// sortable
if (this.frm && this.is_sortable() && !this.sortable_setup_done) {
this.make_sortable($rows);
this.sortable_setup_done = true;
}
this.last_display_status = this.display_status;
this.last_docname = this.frm && this.frm.docname;
// red if mandatory
this.form_grid.toggleClass('error', !!(this.df.reqd && !(this.data && this.data.length)));
this.refresh_remove_rows_button();
this.wrapper.trigger('change');
}
render_result_rows($rows, append_row) {
let result_length = this.grid_pagination.get_result_length();
let page_index = this.grid_pagination.page_index;
let page_length = this.grid_pagination.page_length;
for (var ri = (page_index-1)*page_length; ri < result_length; ri++) {
var d = this.data[ri];
if (!d) {
return;
}
if (d.idx===undefined) {
d.idx = ri + 1;
}
if(this.grid_rows[ri]) {
if (this.grid_rows[ri] && !append_row) {
var grid_row = this.grid_rows[ri];
grid_row.doc = d;
grid_row.refresh();
@ -243,71 +330,49 @@ export default class Grid {
frm: this.frm,
grid: this
});
this.grid_rows.push(grid_row);
this.grid_rows[ri] = grid_row;
}
this.grid_rows_by_docname[d.name] = grid_row;
}
this.wrapper.find(".grid-empty").toggleClass("hidden", Boolean(data.length));
// toolbar
this.setup_toolbar();
this.toggle_checkboxes(this.display_status !== 'Read');
// sortable
if(this.frm && this.is_sortable() && !this.sortable_setup_done) {
this.make_sortable($rows);
this.sortable_setup_done = true;
}
this.last_display_status = this.display_status;
this.last_docname = this.frm && this.frm.docname;
// frappe.utils.scroll_to(_scroll_y);
// red if mandatory
this.form_grid.toggleClass('error', !!(this.df.reqd && !(data && data.length)));
this.refresh_remove_rows_button();
this.wrapper.trigger('change');
}
setup_toolbar() {
if(this.is_editable()) {
this.wrapper.find(".grid-footer").toggle(true);
if (this.is_editable()) {
this.wrapper.find('.grid-footer').toggle(true);
// show, hide buttons to add rows
if(this.cannot_add_rows || (this.df && this.df.cannot_add_rows)) {
if (this.cannot_add_rows || (this.df && this.df.cannot_add_rows)) {
// add 'hidden' to buttons
this.wrapper.find(".grid-add-row, .grid-add-multiple-rows")
this.wrapper.find('.grid-add-row, .grid-add-multiple-rows')
.addClass('hidden');
} else {
// show buttons
this.wrapper.find(".grid-add-row").removeClass('hidden');
this.wrapper.find('.grid-add-row').removeClass('hidden');
if(this.multiple_set) {
this.wrapper.find(".grid-add-multiple-rows").removeClass('hidden');
if (this.multiple_set) {
this.wrapper.find('.grid-add-multiple-rows').removeClass('hidden');
}
}
} else {
this.wrapper.find(".grid-footer").toggle(false);
} else if (this.grid_rows.length < this.grid_pagination.page_length ) {
this.wrapper.find('.grid-footer').toggle(false);
}
}
truncate_rows(data) {
if(this.grid_rows.length > data.length) {
truncate_rows() {
if (this.grid_rows.length > this.data.length) {
// remove extra rows
for(var i=data.length; i < this.grid_rows.length; i++) {
for (var i=this.data.length; i < this.grid_rows.length; i++) {
var grid_row = this.grid_rows[i];
grid_row.wrapper.remove();
}
this.grid_rows.splice(data.length);
this.grid_rows.splice(this.data.length);
}
}
setup_fields() {
var me = this;
// reset docfield
if (this.frm && this.frm.docname) {
// use doc specific docfield object
@ -320,21 +385,23 @@ export default class Grid {
}
}
if(this.doctype && this.frm) {
if (this.doctype && this.frm) {
this.docfields = frappe.meta.get_docfields(this.doctype, this.frm.docname);
} else {
// fields given in docfield
this.docfields = this.df.fields;
}
this.docfields.forEach(function(df) {
me.fields_map[df.fieldname] = df;
this.docfields.forEach(df => {
this.fields_map[df.fieldname] = df;
});
}
refresh_row(docname) {
this.grid_rows_by_docname[docname] &&
this.grid_rows_by_docname[docname].refresh();
}
make_sortable($rows) {
new Sortable($rows.get(0), {
group: {name: this.df.fieldname},
@ -349,14 +416,14 @@ export default class Grid {
}
// prevent drag behaviour if _sortable property is "false"
let idx = $(event.dragged).closest('.grid-row').attr('data-idx');
let doc = this.get_data()[idx - 1];
let doc = this.data[idx%this.grid_pagination.page_length];
if (doc && doc._sortable === false) {
return false;
}
},
onUpdate: (event) => {
let idx = $(event.item).closest('.grid-row').attr('data-idx');
let doc = this.get_data()[idx - 1];
let doc = this.data[idx%this.grid_pagination.page_length];
this.renumber_based_on_dom();
this.frm.script_manager.trigger(this.df.fieldname + "_move", this.df.options, doc.name);
this.refresh();
@ -366,13 +433,15 @@ export default class Grid {
$(this.frm.wrapper).trigger("grid-make-sortable", [this.frm]);
}
get_data() {
var data = this.frm ?
this.frm.doc[this.df.fieldname] || []
: this.df.data || this.get_modal_data();
data.sort(function(a, b) { return a.idx - b.idx});
// data.sort(function(a, b) { return a.idx - b.idx});
return data;
}
get_modal_data() {
return this.df.get_data() ? this.df.get_data().filter(data => {
if (!this.deleted_docs || !in_list(this.deleted_docs, data.name)) {
@ -380,12 +449,12 @@ export default class Grid {
}
}) : [];
}
set_column_disp(fieldname, show) {
if($.isArray(fieldname)) {
var me = this;
for(var i=0, l=fieldname.length; i<l; i++) {
var fname = fieldname[i];
me.get_docfield(fname).hidden = show ? 0 : 1;
this.get_docfield(fname).hidden = show ? 0 : 1;
this.set_editable_grid_column_disp(fname, show);
}
} else {
@ -395,11 +464,12 @@ export default class Grid {
this.refresh(true);
}
set_editable_grid_column_disp(fieldname, show) {
//Hide columns for editable grids
if (this.meta.editable_grid && this.grid_rows) {
this.grid_rows.forEach(function(row) {
row.columns_list.forEach(function(column) {
this.grid_rows.forEach( row => {
row.columns_list.forEach( column => {
//Hide the column specified
if (column.df.fieldname == fieldname) {
if (show) {
@ -433,27 +503,33 @@ export default class Grid {
this.refresh();
}
toggle_reqd(fieldname, reqd) {
this.get_docfield(fieldname).reqd = reqd;
this.refresh();
}
toggle_enable(fieldname, enable) {
this.get_docfield(fieldname).read_only = enable ? 0 : 1;
this.refresh();
}
toggle_display(fieldname, show) {
this.get_docfield(fieldname).hidden = show ? 0 : 1;
this.refresh();
}
toggle_checkboxes(enable) {
this.wrapper.find(".grid-row-check").prop('disabled', !enable)
}
get_docfield(fieldname) {
return frappe.meta.get_docfield(this.doctype, fieldname, this.frm ? this.frm.docname : null);
}
get_row(key) {
if(typeof key == 'number') {
if(key < 0) {
if (typeof key == 'number') {
if (key < 0) {
return this.grid_rows[this.grid_rows.length + key];
} else {
return this.grid_rows[key];
@ -462,9 +538,11 @@ export default class Grid {
return this.grid_rows_by_docname[key];
}
}
get_grid_row(key) {
return this.get_row(key);
}
get_field(fieldname) {
// Note: workaround for get_query
if(!this.fieldinfo[fieldname])
@ -472,16 +550,30 @@ export default class Grid {
}
return this.fieldinfo[fieldname];
}
set_value(fieldname, value, doc) {
if(this.display_status!=="None" && this.grid_rows_by_docname[doc.name]) {
this.grid_rows_by_docname[doc.name].refresh_field(fieldname, value);
}
}
add_new_row(idx, callback, show, copy_doc) {
if(this.is_editable()) {
if(this.frm) {
setup_add_row() {
this.wrapper.find(".grid-add-row").click(() => {
this.add_new_row(null, null, true, null, true);
this.set_focus_on_row();
return false;
});
}
add_new_row(idx, callback, show, copy_doc, go_to_last_page=false) {
if (this.is_editable()) {
if (go_to_last_page) {
this.grid_pagination.go_to_last_page_to_add_row();
}
if (this.frm) {
var d = frappe.model.add_child(this.frm.doc, this.df.options, this.df.fieldname, idx);
if(copy_doc) {
if (copy_doc) {
d = this.duplicate_row(d, copy_doc);
}
d.__unedited = true;
@ -495,13 +587,13 @@ export default class Grid {
this.refresh();
}
if(show) {
if(idx) {
if (show) {
if (idx) {
// always open inserted rows
this.wrapper.find("[data-idx='"+idx+"']").data("grid_row")
.toggle_view(true, callback);
} else {
if(!this.allow_on_grid_editing()) {
if (!this.allow_on_grid_editing()) {
// open last row only if on-grid-editing is disabled
this.wrapper.find(".grid-row:last").data("grid_row")
.toggle_view(true, callback);
@ -515,21 +607,18 @@ export default class Grid {
renumber_based_on_dom() {
// renumber based on dom
let me = this;
let $rows = $(me.parent).find(".rows");
me.grid_rows = [];
me.frm.doc[me.df.fieldname] = [];
$rows.find(".grid-row").each(function(i, item) {
let $rows = $(this.parent).find(".rows");
$rows.find(".grid-row").each( (i, item) => {
let $item = $(item);
let d = locals[me.doctype][$item.attr('data-name')];
d.idx = i + 1;
let index = (this.grid_pagination.page_index-1)*this.grid_pagination.page_length+i;
let d = locals[this.doctype][$item.attr('data-name')];
d.idx = index + 1;
$item.attr('data-idx', d.idx);
me.frm.doc[me.df.fieldname].push(d);
me.grid_rows.push(me.grid_rows_by_docname[d.name]);
this.frm.doc[this.df.fieldname][index] = (d);
this.data[index] = d;
this.grid_rows[index]=(this.grid_rows_by_docname[d.name]);
});
}
@ -545,12 +634,11 @@ export default class Grid {
}
set_focus_on_row(idx) {
var me = this;
if(!idx) {
idx = me.grid_rows.length - 1;
idx = this.grid_rows.length - 1;
}
setTimeout(function() {
me.grid_rows[idx].row
setTimeout(() => {
this.grid_rows[idx].row
.find('input[type="Text"],textarea,select').filter(':visible:first').focus();
}, 100);
}
@ -563,23 +651,22 @@ export default class Grid {
this.visible_columns = [];
for(var ci in fields) {
for (var ci in fields) {
var _df = fields[ci];
// get docfield if from fieldname
df = this.fields_map[_df.fieldname];
if(!df.hidden
if (!df.hidden
&& (this.editable_fields || df.in_list_view)
&& (this.frm && this.frm.get_perm(df.permlevel, "read") || !this.frm)
&& !in_list(frappe.model.layout_fields, df.fieldtype)) {
if(df.columns) {
if (df.columns) {
df.colsize=df.columns;
}
else {
} else {
var colsize = 2;
switch(df.fieldtype) {
switch (df.fieldtype) {
case "Text":
case "Small Text": colsize = 3; break;
case "Check": colsize = 1;
@ -596,7 +683,7 @@ export default class Grid {
}
total_colsize += df.colsize;
if(total_colsize > 11)
if (total_colsize > 11)
return false;
this.visible_columns.push([df, df.colsize]);
}
@ -604,11 +691,11 @@ export default class Grid {
// redistribute if total-col size is less than 12
var passes = 0;
while(total_colsize < 11 && passes < 12) {
for(var i in this.visible_columns) {
while (total_colsize < 11 && passes < 12) {
for (var i in this.visible_columns) {
var df = this.visible_columns[i][0];
var colsize = this.visible_columns[i][1];
if(colsize > 1 && colsize < 11
if (colsize > 1 && colsize < 11
&& !in_list(frappe.model.std_fields_list, df.fieldname)) {
if (passes < 3 && ["Int", "Currency", "Float", "Check", "Percent"].indexOf(df.fieldtype)!==-1) {
@ -620,7 +707,7 @@ export default class Grid {
total_colsize++;
}
if(total_colsize > 10)
if (total_colsize > 10)
break;
}
passes++;
@ -631,18 +718,21 @@ export default class Grid {
is_editable() {
return this.display_status=="Write" && !this.static_rows;
}
is_sortable() {
return this.sortable_status || this.is_editable();
}
only_sortable(status) {
if(status===undefined ? true : status) {
if (status===undefined ? true : status) {
this.sortable_status = true;
this.static_rows = true;
}
}
set_multiple_add(link, qty) {
if(this.multiple_set) return;
var me = this;
if (this.multiple_set) return;
var link_field = frappe.meta.get_docfield(this.df.options, link);
var btn = $(this.wrapper).find(".grid-add-multiple-rows");
@ -650,27 +740,28 @@ export default class Grid {
btn.removeClass('hidden');
// open link selector on click
btn.on("click", function() {
btn.on("click", () => {
new frappe.ui.form.LinkSelector({
doctype: link_field.options,
fieldname: link,
qty_fieldname: qty,
target: me,
target: this,
txt: ""
});
this.grid_pagination.go_to_last_page_to_add_row();
return false;
});
this.multiple_set = true;
}
setup_allow_bulk_edit() {
var me = this;
if(this.frm && this.frm.get_docfield(this.df.fieldname).allow_bulk_edit) {
if (this.frm && this.frm.get_docfield(this.df.fieldname).allow_bulk_edit) {
// download
me.setup_download();
this.setup_download();
// upload
frappe.flags.no_socketio = true;
$(this.wrapper).find(".grid-upload").removeClass('hidden').on("click", function() {
$(this.wrapper).find(".grid-upload").removeClass('hidden').on("click", () => {
new frappe.ui.FileUploader({
as_dataurl: true,
allow_multiple: false,
@ -679,29 +770,29 @@ export default class Grid {
// row #2 contains fieldnames;
var fieldnames = data[2];
me.frm.clear_table(me.df.fieldname);
$.each(data, function(i, row) {
if(i > 6) {
this.frm.clear_table(this.df.fieldname);
$.each(data, (i, row) => {
if (i > 6) {
var blank_row = true;
$.each(row, function(ci, value) {
if(value) {
if (value) {
blank_row = false;
return false;
}
});
if(!blank_row) {
var d = me.frm.add_child(me.df.fieldname);
$.each(row, function(ci, value) {
if (!blank_row) {
var d = this.frm.add_child(this.df.fieldname);
$.each(row, (ci, value) => {
var fieldname = fieldnames[ci];
var df = frappe.meta.get_docfield(me.df.options, fieldname);
var df = frappe.meta.get_docfield(this.df.options, fieldname);
// convert date formatting
if(df.fieldtype==="Date" && value) {
if (df.fieldtype==="Date" && value) {
value = frappe.datetime.user_to_str(value);
}
if(df.fieldtype==="Int" || df.fieldtype==="Check") {
if (df.fieldtype==="Int" || df.fieldtype==="Check") {
value = cint(value);
}
@ -711,18 +802,18 @@ export default class Grid {
}
});
me.frm.refresh_field(me.df.fieldname);
frappe.msgprint({message:__('Table updated'), title:__('Success'), indicator:'green'})
this.frm.refresh_field(this.df.fieldname);
frappe.msgprint({message: __('Table updated'), title: __('Success'), indicator: 'green'});
}
});
return false;
});
}
}
setup_download() {
var me = this;
let title = me.df.label || frappe.model.unscrub(me.df.fieldname);
$(this.wrapper).find(".grid-download").removeClass('hidden').on("click", function() {
let title = this.df.label || frappe.model.unscrub(this.df.fieldname);
$(this.wrapper).find(".grid-download").removeClass('hidden').on("click", () => {
var data = [];
var docfields = [];
data.push([__("Bulk Edit {0}", [title])]);
@ -732,9 +823,9 @@ export default class Grid {
data.push([__("The CSV format is case sensitive")]);
data.push([__("Do not edit headers which are preset in the template")]);
data.push(["------"]);
$.each(frappe.get_meta(me.df.options).fields, function(i, df) {
$.each(frappe.get_meta(this.df.options).fields, (i, df) => {
// don't include the read-only field in the template
if(frappe.model.is_value_type(df.fieldtype)) {
if (frappe.model.is_value_type(df.fieldtype)) {
data[1].push(df.label);
data[2].push(df.fieldname);
let description = (df.description || "") + ' ';
@ -747,13 +838,13 @@ export default class Grid {
});
// add data
$.each(me.frm.doc[me.df.fieldname] || [], function(i, d) {
$.each(this.frm.doc[this.df.fieldname] || [], (i, d) => {
var row = [];
$.each(data[2], function(i, fieldname) {
$.each(data[2], (i, fieldname) => {
var value = d[fieldname];
// format date
if(docfields[i].fieldtype==="Date" && value) {
if (docfields[i].fieldtype==="Date" && value) {
value = frappe.datetime.str_to_user(value);
}
@ -766,10 +857,11 @@ export default class Grid {
return false;
});
}
add_custom_button(label, click) {
// add / unhide a custom button
var btn = this.custom_buttons[label];
if(!btn) {
if (!btn) {
btn = $('<button class="btn btn-default btn-xs btn-custom">' + label + '</button>')
.css('margin-right', '4px')
.prependTo(this.grid_buttons)
@ -779,6 +871,7 @@ export default class Grid {
btn.removeClass('hidden');
}
}
clear_custom_buttons() {
// hide all custom buttons
this.grid_buttons.find('.btn-custom').addClass('hidden');

View file

@ -0,0 +1,132 @@
export default class GridPagination {
constructor(opts) {
$.extend(this, opts);
this.setup_pagination();
}
setup_pagination() {
this.page_length = 20;
this.page_index = 1;
this.total_pages = Math.ceil(this.grid.data.length/this.page_length);
this.render_pagination();
}
render_pagination() {
if (this.grid.data.length <= this.page_length) {
this.wrapper.find('.grid-pagination').html('');
} else {
let $pagination_template = this.get_pagination_html();
this.wrapper.find('.grid-pagination').html($pagination_template);
this.prev_page_button = this.wrapper.find('.prev-page');
this.next_page_button = this.wrapper.find('.next-page');
this.$page_number = this.wrapper.find('.current-page-number');
this.$total_pages = this.wrapper.find('.total-page-number');
this.first_page_button = this.wrapper.find('.first-page');
this.last_page_button = this.wrapper.find('.last-page');
this.bind_pagination_events();
}
}
bind_pagination_events() {
this.prev_page_button.on('click', () => {
this.render_prev_page();
});
this.next_page_button.on('click', () => {
this.render_next_page();
});
this.first_page_button.on('click', () => {
this.go_to_page(1);
});
this.last_page_button.on('click', () => {
this.go_to_page(this.total_pages);
});
}
update_page_numbers() {
let total_pages = Math.ceil(this.grid.data.length/this.page_length);
if (this.total_pages !== total_pages) {
this.total_pages = total_pages;
this.render_pagination();
}
}
check_page_number() {
if (this.page_index > this.total_pages && this.page_index > 1) {
this.go_to_page(this.page_index-1);
}
}
get_pagination_html() {
let page_text_html = `<div class="page-text">
<span class="current-page-number page-number">${__(this.page_index)}</span>
<span>${__('of')}</span>
<span class="total-page-number page-number"> ${__(this.total_pages)} </span>
</div>`;
return $(`<button class="btn btn-default btn-xs first-page"">
<span class="first-page-icon">&laquo;</span>
<span>${__('First')}</span>
</button>
<a class="prev-page">&#8249;</a>
${page_text_html}
<a class="next-page">&#8250;</a>
<button class="btn btn-default btn-xs last-page">
<span>${__('Last')}</span>
<span class="first-page-icon">&raquo;</span>
</button>`);
}
render_next_page() {
if (this.page_index*this.page_length < this.grid.data.length) {
this.page_index++;
this.go_to_page();
}
}
render_prev_page() {
if (this.page_index > 1) {
this.page_index--;
this.go_to_page();
}
}
go_to_page(index) {
if (!index) {
index = this.page_index;
} else {
this.page_index = index;
}
let $rows = $(this.grid.parent).find(".rows").empty();
this.grid.render_result_rows($rows, true);
if (this.$page_number) {
this.$page_number.text(index);
}
this.update_page_numbers();
}
go_to_last_page_to_add_row() {
let total_pages = this.total_pages;
let page_length = this.page_length;
if (this.grid.data.length == page_length*total_pages) {
this.go_to_page(total_pages + 1);
} else {
this.go_to_page(total_pages);
}
frappe.utils.scroll_to(this.wrapper);
}
get_result_length() {
return this.grid.data.length < this.page_index*this.page_length
? this.grid.data.length
: this.page_index*this.page_length;
}
}

View file

@ -34,13 +34,17 @@ frappe.ui.Tags = class {
}
bind() {
const me = this;
const select_tag = function() {
const tagValue = frappe.utils.xss_sanitise(me.$input.val());
me.addTag(tagValue);
me.$input.val('');
}
this.$input.keypress((e) => {
if(e.which == 13 || e.keyCode == 13) {
const tagValue = frappe.utils.xss_sanitise(this.$input.val());
this.addTag(tagValue);
this.$input.val('');
}
if (e.which == 13 || e.keyCode == 13) select_tag();
});
this.$input.focusout(select_tag);
this.$input.on('blur', () => {
this.deactivate();

View file

@ -268,6 +268,32 @@
border-top: 0px;
}
.grid-pagination {
padding: 0;
}
.page-text {
display: inline-block;
}
.prev-page, .next-page {
font-size: 14px;
}
a.prev-page {
padding-left: 15px;
text-decoration: none;
}
a.next-page {
padding-right: 15px;
text-decoration: none;
}
.page-number {
padding: 0 3px;
}
.grid-footer-toolbar {
padding: 10px 15px;
border-top: 1px solid @border-color;

View file

@ -63,3 +63,14 @@ def setup_workflow():
create_todo_workflow()
create_todo_records()
frappe.clear_cache()
@frappe.whitelist()
def create_contact_phone_nos_records():
if frappe.db.get_all('Contact', {'first_name': 'Test Contact'}):
return
doc = frappe.new_doc('Contact')
doc.first_name = 'Test Contact'
for index in range(1000):
doc.append('phone_nos', {'phone': '123456{}'.format(index)})
doc.insert()