[enhancement] Item Selector
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index 370d529..3183b08 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -50,6 +50,7 @@
},
load_defaults: function() {
+ this.frm.show_print_first = true;
if(this.frm.doc.__islocal && this.frm.doc.company) {
frappe.model.set_default_values(this.frm.doc);
$.each(this.frm.doc.accounts || [], function(i, jvd) {
@@ -360,7 +361,7 @@
credit: function(frm, dt, dn) {
cur_frm.cscript.update_totals(frm.doc);
},
-
+
exchange_rate: function(frm, cdt, cdn) {
var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
var row = locals[cdt][cdn];
@@ -368,7 +369,7 @@
if(row.account_currency == company_currency || !frm.doc.multi_currency) {
frappe.model.set_value(cdt, cdn, "exchange_rate", 1);
}
-
+
erpnext.journal_entry.set_debit_credit_in_company_currency(frm, cdt, cdn);
}
})
@@ -404,7 +405,7 @@
frappe.model.set_value(cdt, cdn, "credit",
flt(flt(row.credit_in_account_currency)*row.exchange_rate), precision("credit", row));
-
+
cur_frm.cscript.update_totals(frm.doc);
},
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index b94b3eb..b40e6f4 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -160,10 +160,10 @@
return tax_accounts
-def item_query(doctype, txt, searchfield, start, page_len, filters):
+def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
conditions = []
- return frappe.db.sql("""select tabItem.name,tabItem.item_group,
+ return frappe.db.sql("""select tabItem.name, tabItem.item_group, tabItem.image,
if(length(tabItem.item_name) > 40,
concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name,
if(length(tabItem.description) > 40, \
@@ -192,7 +192,7 @@
"_txt": txt.replace("%", ""),
"start": start,
"page_len": page_len
- })
+ }, as_dict=as_dict)
def bom(doctype, txt, searchfield, start, page_len, filters):
conditions = []
@@ -209,11 +209,11 @@
limit %(start)s, %(page_len)s """.format(
fcond=get_filters_cond(doctype, filters, conditions),
mcond=get_match_cond(doctype),
- key=frappe.db.escape(searchfield)),
+ key=frappe.db.escape(searchfield)),
{
'txt': "%%%s%%" % frappe.db.escape(txt),
'_txt': txt.replace("%", ""),
- 'start': start,
+ 'start': start,
'page_len': page_len
})
@@ -346,13 +346,13 @@
@frappe.whitelist()
def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
from erpnext.controllers.queries import get_match_cond
-
+
if not filters: filters = {}
condition = ""
if filters.get("company"):
condition += "and tabAccount.company = %(company)s"
-
+
return frappe.db.sql("""select tabAccount.name from `tabAccount`
where (tabAccount.report_type = "Profit and Loss"
or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary"))
@@ -360,7 +360,7 @@
and tabAccount.docstatus!=2
and tabAccount.{key} LIKE %(txt)s
{condition} {match_condition}"""
- .format(condition=condition, key=frappe.db.escape(searchfield),
+ .format(condition=condition, key=frappe.db.escape(searchfield),
match_condition=get_match_cond(doctype)), {
'company': filters.get("company", ""),
'txt': "%%%s%%" % frappe.db.escape(txt)
diff --git a/erpnext/public/build.json b/erpnext/public/build.json
index ff8bf94..b3da719 100644
--- a/erpnext/public/build.json
+++ b/erpnext/public/build.json
@@ -18,6 +18,8 @@
"public/js/pos/pos_bill_item.html",
"public/js/pos/pos_item.html",
"public/js/pos/pos_tax_row.html",
- "public/js/pos/pos.js"
+ "public/js/pos/pos.js",
+ "public/js/utils/item_selector.js",
+ "public/js/templates/item_selector.html"
]
}
diff --git a/erpnext/public/css/erpnext.css b/erpnext/public/css/erpnext.css
index 621efb5..d1d26bc 100644
--- a/erpnext/public/css/erpnext.css
+++ b/erpnext/public/css/erpnext.css
@@ -13,17 +13,20 @@
margin: -10px auto;
}
/* pos */
+.pos-item-area {
+ padding: 0px 10px;
+}
+.pos-item-wrapper {
+ padding: 5px;
+}
.pos-item {
- display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
padding: 5px;
- height: 0px;
- padding-bottom: 38%;
- width: 30%;
- margin: 1.6%;
+ padding-bottom: 15px;
border: 1px solid #d1d8dd;
+ margin-bottom: 5px;
}
.pos-item-text {
padding: 0px 5px;
@@ -36,7 +39,13 @@
border: 1px dashed #d1d8dd;
}
.pos-item-image {
- padding-bottom: 100%;
+ width: 100%;
+ height: 0px;
+ padding: 50% 0;
+ text-align: center;
+ line-height: 0;
+ color: #fff;
+ font-size: 30px;
background-size: cover;
border: 1px solid transparent;
background-position: top center;
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index dc6d4cd..9e2bd22 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -8,6 +8,7 @@
erpnext.TransactionController = erpnext.taxes_and_totals.extend({
onload: function() {
var me = this;
+ this.frm.show_print_first = true;
if(this.frm.doc.__islocal) {
var today = get_today(),
currency = frappe.defaults.get_user_default("currency");
@@ -81,7 +82,7 @@
me.calculate_taxes_and_totals();
}
if(frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "item_code")) {
- cur_frm.get_field("items").grid.set_multiple_add("item_code", "qty");
+ this.setup_item_selector();
}
},
@@ -890,7 +891,16 @@
rate = flt(item.rate) * flt(this.frm.doc.conversion_rate || 1);
item.gross_profit = flt(((rate - item.valuation_rate) * item.qty), precision("amount", item));
}
+ },
+
+ setup_item_selector: function() {
+ if(!this.item_selector) {
+ this.item_selector = new erpnext.ItemSelector({frm: this.frm});
+ }
}
+
+
+
});
frappe.ui.form.on(cur_frm.doctype + " Item", "rate", function(frm, cdt, cdn) {
diff --git a/erpnext/public/js/pos/pos.html b/erpnext/public/js/pos/pos.html
index d12b9b2..36ef7c9 100644
--- a/erpnext/public/js/pos/pos.html
+++ b/erpnext/public/js/pos/pos.html
@@ -47,7 +47,10 @@
</div>
</div>
</div>
- <div class="col-sm-7 pos-item-area">
+ <div class="col-sm-7 pos-items-section">
+ <div class="row pos-item-area">
+
+ </div>
<div class="row pos-item-toolbar">
<div class="search-area col-xs-12"></div>
</div>
diff --git a/erpnext/public/js/pos/pos.js b/erpnext/public/js/pos/pos.js
index a239bbc..60801e6 100644
--- a/erpnext/public/js/pos/pos.js
+++ b/erpnext/public/js/pos/pos.js
@@ -131,7 +131,9 @@
item_code: obj.name,
item_price: format_currency(obj.price_list_rate, obj.currency),
item_name: obj.name===obj.item_name ? "" : obj.item_name,
- item_image: obj.image ? "url('" + obj.image + "')" : null
+ item_image: obj.image ? "url('" + obj.image + "')" : null,
+ color: frappe.get_palette(obj.item_name),
+ abbr: frappe.get_abbr(obj.item_name)
})).tooltip().appendTo($wrap);
});
}
diff --git a/erpnext/public/js/pos/pos_item.html b/erpnext/public/js/pos/pos_item.html
index 1235db9..aec36a7 100644
--- a/erpnext/public/js/pos/pos_item.html
+++ b/erpnext/public/js/pos/pos_item.html
@@ -1,9 +1,13 @@
-<div class="pos-item" data-item-code="{%= item_code %}" title="{%= item_name || item_code %}">
- <div class="pos-item-image {% if (!item_image) { %} no-image {% } %}"
- style="{% if (item_image) { %} background-image: {%= item_image %} {% } %}">
+<div class="pos-item-wrapper col-xs-3">
+ <div class="pos-item" data-item-code="{%= item_code %}" title="{%= item_name || item_code %}">
+ <div class="pos-item-image"
+ style="{% if (item_image) { %} background-image: {{ item_image }} {% }
+ else { %} background-color: {{ color }} {% } %}">
+ {% if (!item_image) { %}{{ abbr }}{% } %}
+ </div>
+ <div class="pos-item-text">
+ <h6 class="item-code text-ellipsis">{%= item_name ? (item_name + " (" + item_code + ")") : item_code %}</h6>
+ <div class="small text-ellipsis">{%= item_price %}</div>
+ </div>
</div>
- <div class="pos-item-text">
- <h6 class="item-code text-ellipsis">{%= item_name ? (item_name + " (" + item_code + ")") : item_code %}</h6>
- <div class="small text-ellipsis">{%= item_price %}</div>
- </div>
-</div>
+</div>
\ No newline at end of file
diff --git a/erpnext/public/js/templates/item_selector.html b/erpnext/public/js/templates/item_selector.html
new file mode 100644
index 0000000..47da67c
--- /dev/null
+++ b/erpnext/public/js/templates/item_selector.html
@@ -0,0 +1,16 @@
+<div class="row pos-item-area">
+{% for (var i=0; i < data.length; i++) { var item = data[i]; %}
+<div class="col-xs-3 pos-item-wrapper">
+ <div class="pos-item" data-name="{{ item.name }}">
+ <div class="pos-item-image"
+ {% if(item.image) { %}style="background-image: url({{ item.image }});"{% }
+ else { %}style="background-color: {{ item.color }};"{% } %}>
+ {% if(!item.image) { %}{{ item.abbr }}{% } %}
+ </div>
+ <div class="pos-item-text">
+ <h6 class="item-code text-ellipsis">{{ item.name }}</h6>
+ </div>
+ </div>
+</div>
+{% } %}
+</div>
\ No newline at end of file
diff --git a/erpnext/public/js/utils/item_selector.js b/erpnext/public/js/utils/item_selector.js
new file mode 100644
index 0000000..7d0bd15
--- /dev/null
+++ b/erpnext/public/js/utils/item_selector.js
@@ -0,0 +1,94 @@
+erpnext.ItemSelector = Class.extend({
+ init: function(opts) {
+ $.extend(this, opts);
+
+ this.grid = this.frm.get_field("items").grid;
+ this.setup();
+ },
+
+ setup: function() {
+ var me = this;
+ if(!this.grid.add_items_button) {
+ this.grid.add_items_button = this.grid.add_custom_button(__('Add Items'), function() {
+ if(!me.dialog) {
+ me.make_dialog();
+ }
+ me.dialog.show();
+ me.render_items();
+ });
+ }
+ },
+
+ make_dialog: function() {
+ this.dialog = new frappe.ui.Dialog({
+ title: __('Add Items')
+ });
+ var body = $(this.dialog.body);
+ body.html('<div><p><input type="text" class="form-control"></p>\
+ <br><div class="results"></div></div>');
+
+ this.dialog.input = body.find('.form-control');
+ this.dialog.results = body.find('.results');
+
+ var me = this;
+ this.dialog.results.on('click', '.pos-item', function() {
+ me.add_item($(this).attr('data-name'))
+ });
+
+ this.dialog.input.on('keyup', function() {
+ if(me.timeout_id) {
+ clearTimeout(me.timeout_id);
+ }
+ me.timeout_id = setTimeout(function() {
+ me.render_items();
+ me.timeout_id = undefined;
+ }, 500);
+ });
+ },
+
+ add_item: function(item_code) {
+ // add row or update qty
+ var added = false;
+
+ // find row with item if exists
+ $.each(this.frm.doc.items || [], function(i, d) {
+ if(d.item_code===item_code) {
+ frappe.model.set_value(d.doctype, d.name, 'qty', d.qty + 1);
+ show_alert(__("Added {0} ({1})", [item_code, d.qty]));
+ added = true;
+ return false;
+ }
+ });
+
+ if(!added) {
+ var d = this.grid.add_new_row();
+ frappe.model.set_value(d.doctype, d.name, 'item_code', item_code);
+
+ // after item fetch
+ frappe.after_ajax(function() {
+ setTimeout(function() {
+ frappe.model.set_value(d.doctype, d.name, 'qty', 1);
+ show_alert(__("Added {0} ({1})", [item_code, 1]));
+ }, 100);
+ });
+ }
+
+ },
+
+ render_items: function() {
+ var args = erpnext.queries.item();
+ args.txt = this.dialog.input.val();
+ args.as_dict = 1;
+
+ var me = this;
+ frappe.link_search("Item", args, function(r) {
+ $.each(r.values, function(i, d) {
+ if(!d.image) {
+ d.abbr = frappe.get_abbr(d.item_name);
+ d.color = frappe.get_palette(d.item_name);
+ }
+ });
+ me.dialog.results.html(frappe.render_template('item_selector', {'data':r.values}));
+ });
+ }
+})
\ No newline at end of file
diff --git a/erpnext/public/less/erpnext.less b/erpnext/public/less/erpnext.less
index 813a567..29d1533 100644
--- a/erpnext/public/less/erpnext.less
+++ b/erpnext/public/less/erpnext.less
@@ -16,20 +16,23 @@
}
/* pos */
-.pos {
+
+.pos-item-area {
+ padding: 0px 10px;
+}
+
+.pos-item-wrapper {
+ padding: 5px;
}
.pos-item {
- display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
padding: 5px;
- height: 0px;
- padding-bottom: 38%;
- width: 30%;
- margin: 1.6%;
+ padding-bottom: 15px;
border: 1px solid #d1d8dd;
+ margin-bottom: 5px;
}
.pos-item-text {
@@ -46,7 +49,13 @@
}
.pos-item-image {
- padding-bottom: 100%;
+ width: 100%;
+ height: 0px;
+ padding: 50% 0;
+ text-align: center;
+ line-height: 0;
+ color: #fff;
+ font-size: 30px;
background-size: cover;
border: 1px solid transparent;
background-position: top center;
@@ -130,7 +139,7 @@
.discount-field-col {
padding-left: 0px;
}
-
+
.input-group {
margin-top: 2px;
}
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index 09f29cd..6974eb5 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -300,7 +300,7 @@
{
"allow_on_submit": 0,
"bold": 0,
- "collapsible": 0,
+ "collapsible": 1,
"fieldname": "currency_and_price_list",
"fieldtype": "Section Break",
"hidden": 0,
@@ -308,7 +308,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "",
+ "label": "Currency and Price List",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -525,7 +525,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
- "collapsible_depends_on": "accounts",
+ "collapsible_depends_on": "",
"fieldname": "default_receivable_accounts",
"fieldtype": "Section Break",
"hidden": 0,
@@ -533,7 +533,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Default Receivable Accounts",
+ "label": "Accounting",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -927,7 +927,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-04-06 03:15:14.488537",
+ "modified": "2016-04-07 01:25:25.676480",
"modified_by": "Administrator",
"module": "Selling",
"name": "Customer",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 1cd0852..46f8c70 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -55,12 +55,15 @@
onload_post_render: function() {
var me = this;
- cur_frm.get_field("items").grid.set_multiple_add("item_code", "qty");
this.set_default_account(function() {
if(me.frm.doc.__islocal && me.frm.doc.company && !me.frm.doc.amended_from) {
cur_frm.script_manager.trigger("company");
}
});
+
+ if(!this.item_selector) {
+ this.item_selector = new erpnext.ItemSelector({frm: this.frm});
+ }
},
refresh: function() {