From d7f7382ec0efcf4b3ea202b5d5b3b96d136b58b3 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 17 Feb 2015 16:09:52 +0530 Subject: [PATCH] [grid] added feature for bluk edit --- frappe/public/build.json | 1 + frappe/public/css/form_grid.css | 3 + frappe/public/js/frappe/form/control.js | 2 +- frappe/public/js/frappe/form/grid.js | 98 +++++++++++++------ frappe/public/js/frappe/form/grid_body.html | 28 ++++++ frappe/public/js/frappe/misc/tools.js | 6 +- frappe/public/js/frappe/misc/utils.js | 83 ++++++++++++++++ frappe/public/js/frappe/model/model.js | 4 + frappe/public/js/frappe/ui/messages.js | 6 +- frappe/public/js/frappe/upload.js | 11 +++ .../js/frappe/views/reports/grid_report.js | 4 +- .../js/frappe/views/reports/query_report.js | 2 +- frappe/public/js/legacy/clientscriptAPI.js | 8 +- frappe/public/less/form_grid.less | 4 + 14 files changed, 216 insertions(+), 44 deletions(-) create mode 100644 frappe/public/js/frappe/form/grid_body.html diff --git a/frappe/public/build.json b/frappe/public/build.json index 600984f801..5ff2fb3784 100644 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -132,6 +132,7 @@ "public/js/frappe/form/save.js", "public/js/frappe/form/script_manager.js", "public/js/frappe/form/grid_form.html", + "public/js/frappe/form/grid_body.html", "public/js/frappe/form/grid.js", "public/js/frappe/form/linked_with.js", "public/js/frappe/form/workflow.js", diff --git a/frappe/public/css/form_grid.css b/frappe/public/css/form_grid.css index 1ecf80e4c8..1545642ae0 100644 --- a/frappe/public/css/form_grid.css +++ b/frappe/public/css/form_grid.css @@ -77,6 +77,9 @@ font-size: 120%; border-bottom: 1px solid #d1d8dd; } +.grid-footer { + background-color: #fff; +} .grid-footer-toolbar { padding: 10px 15px; border-top: 1px solid #d1d8dd; diff --git a/frappe/public/js/frappe/form/control.js b/frappe/public/js/frappe/form/control.js index ecd4380325..505ec584d1 100644 --- a/frappe/public/js/frappe/form/control.js +++ b/frappe/public/js/frappe/form/control.js @@ -735,7 +735,7 @@ frappe.ui.form.ControlAttach = frappe.ui.form.ControlData.extend({ this.frm.attachments.update_attachment(attachment); } else { this.set_input(this.fileobj.filename, this.dataurl); - this.refresh(); + //this.refresh(); } }, }); diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index b516b6f10d..0415e86919 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -20,29 +20,16 @@ frappe.ui.form.Grid = Class.extend({ make: function() { var me = this; - this.wrapper = $('
\ -
\ -
\ -
\ -
\ -
'+__("No Data")+'
\ - \ -
\ -
\ -
') + this.wrapper = $(frappe.render_template("grid_body", {})) .appendTo(this.parent) .attr("data-fieldname", this.df.fieldname); $(this.wrapper).find(".grid-add-row").click(function() { me.add_new_row(null, null, true); return false; - }) + }); + + this.setup_allow_bulk_edit(); }, make_head: function() { @@ -144,21 +131,6 @@ frappe.ui.form.Grid = Class.extend({ me.frm.dirty(); } }); - - // $rows.sortable({ - // handle: ".data-row, .grid-form-heading", - // helper: 'clone', - // update: function(event, ui) { - // me.frm.doc[me.df.fieldname] = []; - // $rows.find(".grid-row").each(function(i, item) { - // var doc = $(item).data("doc"); - // doc.idx = i + 1; - // $(this).find(".row-index").html(i + 1); - // me.frm.doc[me.df.fieldname].push(doc); - // }); - // me.frm.dirty(); - // } - // }); }, get_data: function() { var data = this.frm.doc[this.df.fieldname] || []; @@ -231,6 +203,68 @@ frappe.ui.form.Grid = Class.extend({ return false; }); this.multiple_set = true; + }, + setup_allow_bulk_edit: function() { + var me = this; + if(this.frm.get_docfield(this.df.fieldname).allow_bulk_edit) { + // download + me.setup_download(); + + // upload + $(this.wrapper).find(".grid-upload").removeClass("hide").on("click", function() { + frappe.prompt({fieldtype:"Attach", label:"Upload File"}, + function(data) { + var data = frappe.utils.csv_to_array(frappe.upload.get_string(data.upload_file)); + // row #2 contains fieldnames; + var fieldnames = data[2]; + + me.frm.clear_table(me.df.fieldname); + $.each(data, function(i, row) { + if(i > 4) { + var d = me.frm.add_child(me.df.fieldname); + $.each(row, function(ci, value) { + d[fieldnames[ci]] = value; + }); + } + }); + + me.frm.refresh_field(me.df.fieldname); + + }, __("Edit via Upload"), __("Update")); + return false; + }); + } + }, + setup_download: function() { + var me = this; + $(this.wrapper).find(".grid-download").removeClass("hide").on("click", function() { + var data = []; + data.push([__("Bulk Edit {0}", [me.df.label])]); + data.push([]); + data.push([]); + data.push([]); + data.push(["------"]); + $.each(frappe.get_meta(me.df.options).fields, function(i, df) { + if(frappe.model.is_value_type(me.df.fieldtype)) { + data[1].push(df.label); + data[2].push(df.fieldname); + data[3].push(df.description); + } + }); + + // add data + $.each(me.frm.doc[me.df.fieldname] || [], function(i, d) { + row = []; + $.each(data[2], function(i, fieldname) { + row.push(d[fieldname] || ""); + }); + data.push(row); + }); + + frappe.tools.downloadify(data, null, me.df.label); + return false; + }); + } }); diff --git a/frappe/public/js/frappe/form/grid_body.html b/frappe/public/js/frappe/form/grid_body.html new file mode 100644 index 0000000000..55ebb86746 --- /dev/null +++ b/frappe/public/js/frappe/form/grid_body.html @@ -0,0 +1,28 @@ +
+ +
diff --git a/frappe/public/js/frappe/misc/tools.js b/frappe/public/js/frappe/misc/tools.js index d564493c3b..0e25a96c41 100644 --- a/frappe/public/js/frappe/misc/tools.js +++ b/frappe/public/js/frappe/misc/tools.js @@ -3,7 +3,7 @@ frappe.provide("frappe.tools"); -frappe.tools.downloadify = function(data, roles, me) { +frappe.tools.downloadify = function(data, roles, title) { if(roles && roles.length && !has_common(roles, user_roles)) { msgprint(__("Export not allowed. You need {0} role to export.", [frappe.utils.comma_or(roles)])); return; @@ -14,7 +14,7 @@ frappe.tools.downloadify = function(data, roles, me) { var download_from_server = function() { open_url_post("/", { - args: { data: data, filename: me.title }, + args: { data: data, filename: title }, cmd: "frappe.utils.csvutils.send_csv_to_client" }, true); } @@ -33,7 +33,7 @@ frappe.tools.downloadify = function(data, roles, me) { Downloadify.create(id ,{ filename: function(){ - return me.title + '.csv'; + return title + '.csv'; }, data: _get_data, swf: 'assets/frappe/js/lib/downloadify/downloadify.swf', diff --git a/frappe/public/js/frappe/misc/utils.js b/frappe/public/js/frappe/misc/utils.js index c7ddbf79f4..76fcab67f5 100644 --- a/frappe/public/js/frappe/misc/utils.js +++ b/frappe/public/js/frappe/misc/utils.js @@ -320,4 +320,87 @@ frappe.utils = { setTimeout(function() { callback(dataURL); }, 10 ); } }, + + csv_to_array: function (strData, strDelimiter) { + // Check to see if the delimiter is defined. If not, + // then default to comma. + strDelimiter = (strDelimiter || ","); + + // Create a regular expression to parse the CSV values. + var objPattern = new RegExp( + ( + // Delimiters. + "(\\" + strDelimiter + "|\\r?\\n|\\r|^)" + + + // Quoted fields. + "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" + + + // Standard fields. + "([^\"\\" + strDelimiter + "\\r\\n]*))" + ), + "gi" + ); + + + // Create an array to hold our data. Give the array + // a default empty first row. + var arrData = [[]]; + + // Create an array to hold our individual pattern + // matching groups. + var arrMatches = null; + + + // Keep looping over the regular expression matches + // until we can no longer find a match. + while (arrMatches = objPattern.exec( strData )){ + + // Get the delimiter that was found. + var strMatchedDelimiter = arrMatches[ 1 ]; + + // Check to see if the given delimiter has a length + // (is not the start of string) and if it matches + // field delimiter. If id does not, then we know + // that this delimiter is a row delimiter. + if ( + strMatchedDelimiter.length && + strMatchedDelimiter !== strDelimiter + ){ + + // Since we have reached a new row of data, + // add an empty row to our data array. + arrData.push( [] ); + + } + + var strMatchedValue; + + // Now that we have our delimiter out of the way, + // let's check to see which kind of value we + // captured (quoted or unquoted). + if (arrMatches[ 2 ]){ + + // We found a quoted value. When we capture + // this value, unescape any double quotes. + strMatchedValue = arrMatches[ 2 ].replace( + new RegExp( "\"\"", "g" ), + "\"" + ); + + } else { + + // We found a non-quoted value. + strMatchedValue = arrMatches[ 3 ]; + + } + + + // Now that we have our value string, let's add + // it to the data array. + arrData[ arrData.length - 1 ].push( strMatchedValue ); + } + + // Return the parsed data. + return( arrData ); + } }; diff --git a/frappe/public/js/frappe/model/model.js b/frappe/public/js/frappe/model/model.js index 8d006fa42d..0e8ed04e64 100644 --- a/frappe/public/js/frappe/model/model.js +++ b/frappe/public/js/frappe/model/model.js @@ -34,6 +34,10 @@ $.extend(frappe.model, { new_names: {}, events: {}, + is_value_type: function(fieldtype) { + return frappe.model.no_value_type.indexOf(fieldtype)!==-1; + }, + get_std_field: function(fieldname) { var docfield = $.map([].concat(frappe.model.std_fields).concat(frappe.model.std_fields_table), function(d) { diff --git a/frappe/public/js/frappe/ui/messages.js b/frappe/public/js/frappe/ui/messages.js index 3b28fc5095..6b6ff48ef5 100644 --- a/frappe/public/js/frappe/ui/messages.js +++ b/frappe/public/js/frappe/ui/messages.js @@ -34,13 +34,13 @@ frappe.confirm = function(message, ifyes, ifno) { return d; } -frappe.prompt = function(fields, callback) { +frappe.prompt = function(fields, callback, title, primary_label) { if(!$.isArray(fields)) fields = [fields]; var d = new frappe.ui.Dialog({ fields: fields, - title: __("Enter Value"), + title: title || __("Enter Value"), }) - d.set_primary_action(__("Submit"), function() { + d.set_primary_action(primary_label || __("Submit"), function() { var values = d.get_values(); if(!values) { return; diff --git a/frappe/public/js/frappe/upload.js b/frappe/public/js/frappe/upload.js index 2a09c9a3d8..7f68a75dc9 100644 --- a/frappe/public/js/frappe/upload.js +++ b/frappe/public/js/frappe/upload.js @@ -116,5 +116,16 @@ frappe.upload = { freader.readAsDataURL(fileobj); } + }, + get_string: function(dataURI) { + // remove filename + var parts = dataURI.split(','); + if(parts[0].indexOf(":")===-1) { + var a = parts[2]; + } else { + var a = parts[1]; + } + + return atob(a); } } diff --git a/frappe/public/js/frappe/views/reports/grid_report.js b/frappe/public/js/frappe/views/reports/grid_report.js index 8a5c2291a8..de3d5efcd7 100644 --- a/frappe/public/js/frappe/views/reports/grid_report.js +++ b/frappe/public/js/frappe/views/reports/grid_report.js @@ -444,7 +444,7 @@ frappe.views.GridReport = Class.extend({ }, export: function() { frappe.tools.downloadify(frappe.slickgrid_tools.get_view_data(this.columns, this.dataView), - ["Report Manager", "System Manager"], this); + ["Report Manager", "System Manager"], this.title); return false; }, apply_filters: function(item) { @@ -915,7 +915,7 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({ return false; }); - frappe.tools.downloadify(data, ["Report Manager", "System Manager"], me); + frappe.tools.downloadify(data, ["Report Manager", "System Manager"], me.title); return false; }) diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index e6615f0fd5..30d294ec0c 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -622,7 +622,7 @@ frappe.views.QueryReport = Class.extend({ return [row.splice(1)]; }); this.title = this.report_name; - frappe.tools.downloadify(result, null, this); + frappe.tools.downloadify(result, null, this.title); return false; } }) diff --git a/frappe/public/js/legacy/clientscriptAPI.js b/frappe/public/js/legacy/clientscriptAPI.js index d5ae8978bd..975a95c453 100644 --- a/frappe/public/js/legacy/clientscriptAPI.js +++ b/frappe/public/js/legacy/clientscriptAPI.js @@ -146,11 +146,15 @@ _f.Frm.prototype.field_map = function(fnames, fn) { } } +_f.Frm.prototype.get_docfield = function(fieldname) { + return frappe.meta.get_docfield(this.doctype, fieldname, this.docname); +} + _f.Frm.prototype.set_df_property = function(fieldname, property, value) { - var field = frappe.meta.get_docfield(cur_frm.doctype, fieldname, cur_frm.docname) + var field = this.get_docfield(fieldname); if(field) { field[property] = value; - cur_frm.refresh_field(fieldname); + this.refresh_field(fieldname); }; } diff --git a/frappe/public/less/form_grid.less b/frappe/public/less/form_grid.less index a40cbb95c6..8d39b9f87d 100644 --- a/frappe/public/less/form_grid.less +++ b/frappe/public/less/form_grid.less @@ -91,6 +91,10 @@ border-bottom: 1px solid @border-color; } +.grid-footer { + background-color: #fff; +} + .grid-footer-toolbar { padding: 10px 15px; border-top: 1px solid @border-color;