diff --git a/frappe/desk/doctype/kanban_board/kanban_board.py b/frappe/desk/doctype/kanban_board/kanban_board.py
index cc544c9f68..43bb5a41a8 100644
--- a/frappe/desk/doctype/kanban_board/kanban_board.py
+++ b/frappe/desk/doctype/kanban_board/kanban_board.py
@@ -8,6 +8,7 @@ import json
from frappe import _
from frappe.model.document import Document
from six import iteritems
+from frappe.custom.doctype.custom_field.custom_field import create_custom_field
class KanbanBoard(Document):
@@ -36,6 +37,14 @@ def has_permission(doc, ptype, user):
return False
+@frappe.whitelist()
+def get_kanban_boards(doctype):
+ '''Get Kanban Boards for doctype to show in List View'''
+ return frappe.get_list('Kanban Board',
+ fields=['name', 'filters', 'reference_doctype', 'private'],
+ filters={ 'reference_doctype': doctype }
+ )
+
@frappe.whitelist()
def add_column(board_name, column_title):
'''Adds new column to Kanban Board'''
@@ -120,6 +129,14 @@ def quick_kanban_board(doctype, board_name, field_name, project=None):
'''Create new KanbanBoard quickly with default options'''
doc = frappe.new_doc('Kanban Board')
+ if field_name == 'kanban_column':
+ create_custom_field(doctype, {
+ 'label': 'Kanban Column',
+ 'fieldname': 'kanban_column',
+ 'fieldtype': 'Select',
+ 'hidden': 1
+ })
+
meta = frappe.get_meta(doctype)
options = ''
diff --git a/frappe/desk/form/meta.py b/frappe/desk/form/meta.py
index b6f95ec981..3db46aeda0 100644
--- a/frappe/desk/form/meta.py
+++ b/frappe/desk/form/meta.py
@@ -51,7 +51,7 @@ class FormMeta(Meta):
for k in ("__js", "__css", "__list_js", "__calendar_js", "__map_js",
"__linked_with", "__messages", "__print_formats", "__workflow_docs",
"__form_grid_templates", "__listview_template", "__tree_js",
- "__dashboard", "__kanban_boards", "__kanban_column_fields", '__templates',
+ "__dashboard", "__kanban_column_fields", '__templates',
'__custom_js'):
d[k] = self.get(k)
@@ -185,14 +185,8 @@ class FormMeta(Meta):
self.set('__dashboard', self.get_dashboard_data())
def load_kanban_meta(self):
- self.load_kanban_boards()
self.load_kanban_column_fields()
- def load_kanban_boards(self):
- kanban_boards = frappe.get_list(
- 'Kanban Board', fields=['name', 'filters', 'reference_doctype', 'private'], filters={'reference_doctype': self.name})
- self.set("__kanban_boards", kanban_boards, as_value=True)
-
def load_kanban_column_fields(self):
values = frappe.get_list(
'Kanban Board', fields=['field_name'],
diff --git a/frappe/public/js/frappe/list/list_sidebar.js b/frappe/public/js/frappe/list/list_sidebar.js
index e2717735dc..ba03fd18de 100644
--- a/frappe/public/js/frappe/list/list_sidebar.js
+++ b/frappe/public/js/frappe/list/list_sidebar.js
@@ -9,14 +9,15 @@ frappe.provide('frappe.views');
// parent
// set_filter = function called on click
-frappe.views.ListSidebar = Class.extend({
- init: function(opts) {
+frappe.views.ListSidebar = class ListSidebar {
+ constructor(opts) {
$.extend(this, opts);
this.make();
this.get_stats();
this.cat_tags = [];
- },
- make: function() {
+ }
+
+ make() {
var sidebar_content = frappe.render_template("list_sidebar", { doctype: this.doctype });
this.sidebar = $('
')
@@ -36,8 +37,9 @@ frappe.views.ListSidebar = Class.extend({
if (limits.upgrade_url && limits.expiry && !frappe.flags.upgrade_dismissed) {
this.setup_upgrade_box();
}
- },
- setup_views: function() {
+ }
+
+ setup_views() {
var show_list_link = false;
if (frappe.views.calendar[this.doctype]) {
@@ -85,8 +87,9 @@ frappe.views.ListSidebar = Class.extend({
if (show_list_link) {
this.sidebar.find('.list-link[data-view="List"]').removeClass('hide');
}
- },
- setup_reports: function() {
+ }
+
+ setup_reports() {
// add reports linked to this doctype to the dropdown
var me = this;
var added = [];
@@ -124,167 +127,22 @@ frappe.views.ListSidebar = Class.extend({
// from specially tagged reports
add_reports(frappe.boot.user.all_reports || []);
- },
+ }
- setup_list_filter: function() {
+ setup_list_filter() {
this.list_filter = new ListFilter({
wrapper: this.page.sidebar.find('.list-filters'),
doctype: this.doctype,
list_view: this.list_view
});
- },
+ }
- setup_kanban_boards: function() {
- // add kanban boards linked to this doctype to the dropdown
- var me = this;
- var $dropdown = this.page.sidebar.find('.kanban-dropdown');
- var divider = false;
+ setup_kanban_boards() {
+ const $dropdown = this.page.sidebar.find('.kanban-dropdown');
+ frappe.views.KanbanView.setup_dropdown_in_sidebar(this.doctype, $dropdown);
+ }
- var meta = frappe.get_meta(this.doctype);
- var boards = meta && meta.__kanban_boards;
- if (!boards) return;
-
- boards.forEach(function(board) {
- var route = ["List", board.reference_doctype, "Kanban", board.name].join('/');
- if (!divider) {
- me.get_divider().appendTo($dropdown);
- divider = true;
- }
- $(`
- ${__(board.name)}
- ${board.private ? '' : ''}
- `).appendTo($dropdown);
- });
-
- $dropdown.find('.new-kanban-board').click(function() {
- // frappe.new_doc('Kanban Board', {reference_doctype: me.doctype});
- var select_fields = frappe.get_meta(me.doctype)
- .fields.filter(function(df) {
- return df.fieldtype === 'Select' &&
- df.fieldname !== 'kanban_column';
- });
-
- var fields = [{
- fieldtype: 'Data',
- fieldname: 'board_name',
- label: __('Kanban Board Name'),
- reqd: 1
- }];
-
- if(me.doctype === 'Task') {
- fields.push({
- fieldtype: 'Link',
- fieldname: 'project',
- label: __('Project'),
- options: 'Project'
- });
- }
-
- if(select_fields.length > 0) {
- fields = fields.concat([{
- fieldtype: 'Select',
- fieldname: 'field_name',
- label: __('Columns based on'),
- options: select_fields.map(df => df.label).join('\n'),
- default: select_fields[0],
- depends_on: 'eval:doc.custom_column===0'
- },
- {
- fieldtype: 'Check',
- fieldname: 'custom_column',
- label: __('Custom Column'),
- default: 0
- }]);
- }
-
- if(['Note', 'ToDo'].includes(me.doctype)) {
- fields[0].description = __('This Kanban Board will be private');
- }
-
- var d = new frappe.ui.Dialog({
- title: __('New Kanban Board'),
- fields: fields,
- primary_action_label: __('Save'),
- primary_action: function(values) {
-
- var custom_column = values.custom_column !== undefined ?
- values.custom_column : 1;
- var field_name;
- if (custom_column) {
- field_name = 'kanban_column';
- } else {
-
- if (!values.field_name) {
- frappe.throw(__('Please select Columns Based On'));
- }
- var field_name =
- select_fields
- .find(df => df.label === values.field_name)
- .fieldname;
- }
-
- me.add_custom_column_field(custom_column)
- .then(function(custom_column) {
- return me.make_kanban_board(values.board_name, field_name, values.project);
- })
- .then(function() {
- d.hide();
- }, function(err) {
- frappe.msgprint(err);
- });
- }
- });
- d.show();
- });
- },
- add_custom_column_field: function(flag) {
- var me = this;
- return new Promise(function(resolve, reject) {
- if (!flag) resolve(false);
- frappe.call({
- method: 'frappe.custom.doctype.custom_field.custom_field.add_custom_field',
- args: {
- doctype: me.doctype,
- df: {
- label: 'Kanban Column',
- fieldname: 'kanban_column',
- fieldtype: 'Select',
- hidden: 1
- }
- }
- }).success(function() {
- resolve(true);
- }).error(function(err) {
- reject(err);
- });
- });
- },
- make_kanban_board: function(board_name, field_name, project) {
- var me = this;
- return frappe.call({
- method: 'frappe.desk.doctype.kanban_board.kanban_board.quick_kanban_board',
- args: {
- doctype: me.doctype,
- board_name,
- field_name,
- project
- },
- callback: function(r) {
- var kb = r.message;
- if (kb.filters) {
- frappe.provide('frappe.kanban_filters');
- frappe.kanban_filters[kb.kanban_board_name] = kb.filters;
- }
- frappe.set_route(
- 'List',
- me.doctype,
- 'Kanban',
- kb.kanban_board_name
- );
- }
- });
- },
- setup_calendar_view: function() {
+ setup_calendar_view() {
const doctype = this.doctype;
frappe.db.get_list('Calendar View', {
@@ -322,8 +180,9 @@ frappe.views.ListSidebar = Class.extend({
$link_calendar.removeClass('hide');
$link_calendar.html(dropdown_html);
});
- },
- setup_email_inbox: function() {
+ }
+
+ setup_email_inbox() {
// get active email account for the user and add in dropdown
if (this.doctype != "Communication")
return;
@@ -352,13 +211,15 @@ frappe.views.ListSidebar = Class.extend({
$dropdown.find('.new-email-account').click(function() {
frappe.new_doc("Email Account");
});
- },
- setup_assigned_to_me: function() {
+ }
+
+ setup_assigned_to_me() {
this.page.sidebar.find(".assigned-to-me a").on("click", () => {
this.list_view.filter_area.add(this.list_view.doctype, "_assign", "like", `%${frappe.session.user}%`);
});
- },
- setup_upgrade_box: function() {
+ }
+
+ setup_upgrade_box() {
let upgrade_list = $(``).appendTo(this.sidebar);
// Show Renew/Upgrade button,
@@ -386,11 +247,13 @@ frappe.views.ListSidebar = Class.extend({
frappe.flags.upgrade_dismissed = 1;
});
}
- },
- get_cat_tags: function() {
+ }
+
+ get_cat_tags() {
return this.cat_tags;
- },
- get_stats: function() {
+ }
+
+ get_stats() {
var me = this;
frappe.call({
method: 'frappe.desk.reportview.get_sidebar_stats',
@@ -425,8 +288,9 @@ frappe.views.ListSidebar = Class.extend({
}
}
});
- },
- render_stat: function(field, stat, tags) {
+ }
+
+ render_stat(field, stat, tags) {
var me = this;
var sum = 0;
var stats = [];
@@ -485,8 +349,9 @@ frappe.views.ListSidebar = Class.extend({
});
})
.insertBefore(this.sidebar.find(".close-sidebar-button"));
- },
- set_fieldtype: function(df) {
+ }
+
+ set_fieldtype(df) {
// scrub
if (df.fieldname == "docstatus") {
@@ -511,12 +376,14 @@ frappe.views.ListSidebar = Class.extend({
if (df.fieldtype === "Data" && (df.options || "").toLowerCase() === "email") {
df.options = null;
}
- },
- reload_stats: function() {
+ }
+
+ reload_stats() {
this.sidebar.find(".sidebar-stat").remove();
this.get_stats();
- },
- get_divider: function() {
+ }
+
+ get_divider() {
return $('');
}
-});
\ No newline at end of file
+};
diff --git a/frappe/public/js/frappe/views/kanban/kanban_view.js b/frappe/public/js/frappe/views/kanban/kanban_view.js
index dcc69e8db1..7b163baf97 100644
--- a/frappe/public/js/frappe/views/kanban/kanban_view.js
+++ b/frappe/public/js/frappe/views/kanban/kanban_view.js
@@ -72,7 +72,7 @@ frappe.views.KanbanView = class KanbanView extends frappe.views.ListView {
render() {
const board_name = this.board_name;
- if(this.kanban && board_name === this.kanban.board_name) {
+ if (this.kanban && board_name === this.kanban.board_name) {
this.kanban.update(this.data);
return;
}
@@ -95,14 +95,14 @@ frappe.views.KanbanView = class KanbanView extends frappe.views.ListView {
var title_field = null;
var quick_entry = false;
- if(this.meta.title_field) {
+ if (this.meta.title_field) {
title_field = frappe.meta.get_field(this.doctype, this.meta.title_field);
}
- this.meta.fields.forEach(function(df) {
+ this.meta.fields.forEach((df) => {
const is_valid_field =
in_list(['Data', 'Text', 'Small Text', 'Text Editor'], df.fieldtype)
- && !df.hidden;
+ && !df.hidden;
if (is_valid_field && !title_field) {
// can be mapped to textarea
@@ -111,15 +111,13 @@ frappe.views.KanbanView = class KanbanView extends frappe.views.ListView {
});
// quick entry
- var mandatory = meta.fields.filter(function(df) {
- return df.reqd && !doc[df.fieldname];
- });
+ var mandatory = meta.fields.filter((df) => df.reqd && !doc[df.fieldname]);
- if(mandatory.some(df => df.fieldtype === 'Table') || mandatory.length > 1) {
+ if (mandatory.some(df => df.fieldtype === 'Table') || mandatory.length > 1) {
quick_entry = true;
}
- if(!title_field) {
+ if (!title_field) {
title_field = frappe.meta.get_field(this.doctype, 'name');
}
@@ -136,3 +134,135 @@ frappe.views.KanbanView = class KanbanView extends frappe.views.ListView {
];
}
};
+
+
+frappe.views.KanbanView.setup_dropdown_in_sidebar = function(doctype, $dropdown) {
+ // get kanban boards and append to dropdown
+ get_kanban_boards()
+ .then((kanban_boards) => {
+ if (!kanban_boards) return;
+
+ $('').appendTo($dropdown);
+
+ kanban_boards.forEach(board => {
+ const route = ['List', board.reference_doctype, 'Kanban', board.name].join('/');
+
+ $(`
+
+ ${__(board.name)}
+ ${board.private ? '' : ''}
+
+
+ `).appendTo($dropdown);
+ });
+ });
+
+ $dropdown.on('click', '.new-kanban-board', () => {
+ const dialog = new_kanban_dialog();
+ dialog.show();
+ });
+
+ function make_kanban_board(board_name, field_name, project) {
+ return frappe.call({
+ method: 'frappe.desk.doctype.kanban_board.kanban_board.quick_kanban_board',
+ args: {
+ doctype,
+ board_name,
+ field_name,
+ project
+ },
+ callback: function(r) {
+ var kb = r.message;
+ if (kb.filters) {
+ frappe.provide('frappe.kanban_filters');
+ frappe.kanban_filters[kb.kanban_board_name] = kb.filters;
+ }
+ frappe.set_route('List', doctype, 'Kanban', kb.kanban_board_name);
+ }
+ });
+ }
+
+ let dialog = null;
+
+ function new_kanban_dialog() {
+ if (dialog) return dialog;
+
+ const fields = get_fields_for_dialog();
+
+ dialog = new frappe.ui.Dialog({
+ title: __('New Kanban Board'),
+ fields: fields,
+
+ primary_action_label: __('Save'),
+ primary_action(values) {
+ const custom_column =
+ values.custom_column !== undefined ?
+ values.custom_column : 1;
+
+ let field_name = custom_column ? 'kanban_column' : values.field_name;
+
+ make_kanban_board(values.board_name, field_name, values.project)
+ .then(() => dialog.hide(), (err) => frappe.msgprint(err));
+ }
+ });
+ return dialog;
+ }
+
+ function get_fields_for_dialog() {
+
+ let fields = [{
+ fieldtype: 'Data',
+ fieldname: 'board_name',
+ label: __('Kanban Board Name'),
+ reqd: 1,
+ description: ['Note', 'ToDo'].includes(doctype) ?
+ __('This Kanban Board will be private') : ''
+ }];
+
+ if (doctype === 'Task') {
+ fields.push({
+ fieldtype: 'Link',
+ fieldname: 'project',
+ label: __('Project'),
+ options: 'Project'
+ });
+ }
+
+ const select_fields =
+ frappe.get_meta(doctype).fields
+ .filter(df => {
+ return df.fieldtype === 'Select' &&
+ df.fieldname !== 'kanban_column';
+ });
+
+ if (select_fields.length > 0) {
+ fields = fields.concat([{
+ fieldtype: 'Select',
+ fieldname: 'field_name',
+ label: __('Columns based on'),
+ options: select_fields.map(df => ({label: df.label, value: df.fieldname})),
+ default: select_fields[0],
+ depends_on: 'eval:doc.custom_column===0',
+ reqd: 1
+ },
+
+ {
+ fieldtype: 'Check',
+ fieldname: 'custom_column',
+ label: __('Custom Column'),
+ default: 0,
+ onchange() {
+ const value = this.get_value();
+ this.layout.set_df_property('field_name', 'reqd', !value);
+ }
+ }]);
+ }
+
+ return fields;
+ }
+
+ function get_kanban_boards() {
+ return frappe.call('frappe.desk.doctype.kanban_board.kanban_board.get_kanban_boards', { doctype })
+ .then(r => r.message);
+ }
+};