diff --git a/erpnext/__init__.py b/erpnext/__init__.py index cf6ca02e7f7..542743dde20 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from erpnext.hooks import regional_overrides -__version__ = '8.10.0' +__version__ = '8.10.1' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py index adb7bc13385..1e694e7e3a7 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py +++ b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py @@ -78,6 +78,7 @@ def get_chart(chart_template, existing_company=None): for folder in folders: path = os.path.join(os.path.dirname(__file__), folder) for fname in os.listdir(path): + fname = frappe.as_unicode(fname) if fname.endswith(".json"): with open(os.path.join(path, fname), "r") as f: chart = f.read() @@ -105,6 +106,7 @@ def get_charts_for_country(country): path = os.path.join(os.path.dirname(__file__), folder) for fname in os.listdir(path): + fname = frappe.as_unicode(fname) if (fname.startswith(country_code) or fname.startswith(country)) and fname.endswith(".json"): with open(os.path.join(path, fname), "r") as f: _get_chart_name(f.read()) diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/fr_plan_comptable_général.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/fr_plan_comptable_general.json similarity index 99% rename from erpnext/accounts/doctype/account/chart_of_accounts/verified/fr_plan_comptable_général.json rename to erpnext/accounts/doctype/account/chart_of_accounts/verified/fr_plan_comptable_general.json index d7b464a3816..f6015f37442 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/fr_plan_comptable_général.json +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/fr_plan_comptable_general.json @@ -1607,4 +1607,4 @@ "root_type": "Income" } } -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index f36fdf79071..2b3459ad273 100644 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -320,8 +320,7 @@ def make_invoice(doc_list={}, email_queue_list={}, customers_list={}): si_doc.set_posting_time = 1 si_doc.customer = get_customer_id(doc) si_doc.due_date = doc.get('posting_date') - submit_invoice(si_doc, name, doc) - name_list.append(name) + name_list = submit_invoice(si_doc, name, doc, name_list) else: name_list.append(name) @@ -475,19 +474,29 @@ def validate_item(doc): frappe.db.commit() -def submit_invoice(si_doc, name, doc): +def submit_invoice(si_doc, name, doc, name_list): try: si_doc.insert() si_doc.submit() frappe.db.commit() + name_list.append(name) except Exception as e: if frappe.message_log: frappe.message_log.pop() frappe.db.rollback() - save_invoice(e, si_doc, name) + frappe.log_error(frappe.get_traceback()) + name_list = save_invoice(e, si_doc, name, name_list) -def save_invoice(e, si_doc, name): - if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}): - si_doc.docstatus = 0 - si_doc.flags.ignore_mandatory = True - si_doc.due_date = si_doc.posting_date - si_doc.insert() + return name_list + +def save_invoice(e, si_doc, name, name_list): + try: + if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}): + si_doc.docstatus = 0 + si_doc.flags.ignore_mandatory = True + si_doc.due_date = si_doc.posting_date + si_doc.insert() + name_list.append(name) + except Exception: + frappe.log_error(frappe.get_traceback()) + + return name_list diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index d69a3066706..33b41e9ee44 100644 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -426,11 +426,16 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }); this.serach_item.make_input(); - this.serach_item.$input.on("keyup", function () { - setTimeout(function () { - me.items = me.get_items(); - me.make_item_list(); - }, 1000); + + this.serach_item.$input.on("keypress", function (event) { + + clearTimeout(me.last_search_timeout); + me.last_search_timeout = setTimeout(() => { + if((me.serach_item.$input.val() != "") || (event.which == 13)) { + me.items = me.get_items(); + me.make_item_list(); + } + }, 400); }); this.search_item_group = this.wrapper.find('.search-item-group'); @@ -727,14 +732,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ input = input.toLowerCase(); item = this.get_item(item.value); - var searchtext = - Object.keys(item) - .filter(key => ['customer_name', 'customer_group', 'value', 'label', 'email_id', 'phone', 'mobile_no'].includes(key)) - .map(key => item[key]) - .join(" ") - .toLowerCase(); - - return searchtext.includes(input) + return item.searchtext.includes(input) }, item: function (item, input) { var d = this.get_item(item.value); @@ -808,7 +806,11 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ territory: c.territory, phone: contact ? contact["phone"] : '', mobile_no: contact ? contact["mobile_no"] : '', - email_id: contact ? contact["email_id"] : '' + email_id: contact ? contact["email_id"] : '', + searchtext: ['customer_name', 'customer_group', 'value', + 'label', 'email_id', 'phone', 'mobile_no'] + .map(key => c[key]).join(' ') + .toLowerCase() } }); diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index eaf064b26c8..a333ca82d68 100644 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -125,7 +125,33 @@ $.extend(erpnext.utils, { } }); } - } + }, + + /** + * Checks if the first row of a given child table is empty + * @param child_table - Child table Doctype + * @return {Boolean} + **/ + first_row_is_empty: function(child_table){ + if($.isArray(child_table) && child_table.length > 0) { + return !child_table[0].item_code; + } + return false; + }, + + /** + * Removes the first row of a child table if it is empty + * @param {_Frm} frm - The current form + * @param {String} child_table_name - The child table field name + * @return {Boolean} + **/ + remove_empty_first_row: function(frm, child_table_name){ + const rows = frm['doc'][child_table_name]; + if (this.first_row_is_empty(rows)){ + frm['doc'][child_table_name] = rows.splice(1); + } + return rows; + }, }); erpnext.utils.map_current_doc = function(opts) { diff --git a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py index d4c9df380f3..a477379deda 100644 --- a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py +++ b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py @@ -44,4 +44,5 @@ class TestCurrencyExchange(unittest.TestCase): # Exchange rate as on 15th Dec, 2015, should be fetched from fixer.io exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15") - self.assertFalse(exchange_rate==60) \ No newline at end of file + self.assertFalse(exchange_rate == 60) + self.assertEqual(exchange_rate, 66.894) \ No newline at end of file diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index 888099fc4f4..bdbf3f4ec2b 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -83,7 +83,8 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None): if not value: import requests - response = requests.get("http://api.fixer.io/latest", params={ + api_url = "http://api.fixer.io/{0}".format(transaction_date) + response = requests.get(api_url, params={ "base": from_currency, "symbols": to_currency }) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index fc0b9b21064..7043fb7ba8e 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -153,9 +153,11 @@ erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.exten if(!r.message) { frappe.throw(__("BOM does not contain any stock item")) } else { + erpnext.utils.remove_empty_first_row(cur_frm, "items"); $.each(r.message, function(i, item) { var d = frappe.model.add_child(cur_frm.doc, "Material Request Item", "items"); d.item_code = item.item_code; + d.item_name = item.item_name; d.description = item.description; d.warehouse = values.warehouse; d.uom = item.stock_uom; diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js b/erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js new file mode 100644 index 00000000000..d8b39fe5aaf --- /dev/null +++ b/erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js @@ -0,0 +1,28 @@ +QUnit.module('manufacturing'); + +QUnit.test("test material request get items from BOM", function(assert) { + assert.expect(4); + let done = assert.async(); + frappe.run_serially([ + () => frappe.set_route('Form', 'BOM'), + () => frappe.timeout(3), + () => frappe.click_button('Get Items from BOM'), + () => frappe.timeout(3), + () => { + assert.ok(cur_dialog, 'dialog appeared'); + }, + () => cur_dialog.set_value('bom', 'Laptop'), + () => cur_dialog.set_value('warehouse', 'Laptop Scrap Warehouse'), + () => frappe.click_button('Get Items from BOM'), + () => frappe.timeout(3), + () => { + assert.ok(cur_frm.doc.items[0].item_code, "First row is not empty"); + assert.ok(cur_frm.doc.items[0].item_name, "Item name is not empty"); + assert.equal(cur_frm.doc.items[0].item_name, "Laptop", cur_frm.doc.items[0].item_name); + }, + () => cur_frm.doc.items[0].schedule_date = '2017-12-12', + () => cur_frm.save(), + () => done() + ]); +}); +