diff --git a/.travis.yml b/.travis.yml index 40afeee8d46..a8dc872adff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -76,5 +76,6 @@ install: - bench --site test_site reinstall --yes after_script: + - pip install coverage==4.5.4 - pip install python-coveralls - coveralls -b apps/erpnext -d ../../sites/.coverage diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 2cdb6f4bbe3..63d1e97cfe2 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '11.1.72' +__version__ = '11.1.73' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index 97d15130eb0..9285a5dada2 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -47,8 +47,8 @@ class Task(NestedSet): def validate_status(self): if self.status!=self.get_db_value("status") and self.status == "Closed": for d in self.depends_on: - if frappe.db.get_value("Task", d.task, "status") != "Closed": - frappe.throw(_("Cannot close task as its dependant task {0} is not closed.").format(d.task)) + if frappe.db.get_value("Task", d.task, "status") not in ("Closed", "Cancelled"): + frappe.throw(_("Cannot close task as its dependant task {0} is not closed/cancelled.").format(d.task)) from frappe.desk.form.assign_to import clear clear(self.doctype, self.name) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index d29750950d7..0a441d95746 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -853,15 +853,19 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, conversion_factor: function(doc, cdt, cdn, dont_fetch_price_list_rate) { - if(doc.doctype != 'Material Request' && frappe.meta.get_docfield(cdt, "stock_qty", cdn)) { + if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) { var item = frappe.get_doc(cdt, cdn); frappe.model.round_floats_in(item, ["qty", "conversion_factor"]); item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item)); - item.total_weight = flt(item.stock_qty * item.weight_per_unit); refresh_field("stock_qty", item.name, item.parentfield); - refresh_field("total_weight", item.name, item.parentfield); this.toggle_conversion_factor(item); - this.calculate_net_weight(); + + if(doc.doctype != "Material Request") { + item.total_weight = flt(item.stock_qty * item.weight_per_unit); + refresh_field("total_weight", item.name, item.parentfield); + this.calculate_net_weight(); + } + if (!dont_fetch_price_list_rate && frappe.meta.has_field(doc.doctype, "price_list_currency")) { this.apply_price_list(item, true); diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index fff07261053..88907886286 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -109,7 +109,7 @@ def get_product_list_for_group(product_group=None, start=0, limit=10, search=Non or I.name like %(search)s)""" search = "%" + cstr(search) + "%" - query += """order by I.weightage desc, in_stock desc, I.modified desc limit %s, %s""" % (start, limit) + query += """order by I.weightage desc, in_stock desc, I.modified desc limit %s, %s""" % (cint(start), cint(limit)) data = frappe.db.sql(query, {"product_group": product_group,"search": search, "today": nowdate()}, as_dict=1) data = adjust_qty_for_expired_items(data) diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index 2e1636ab43b..a93c3ed3932 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -124,7 +124,7 @@ class LandedCostVoucher(Document): # update stock & gl entries for submit state of PR doc.docstatus = 1 - doc.update_stock_ledger(via_landed_cost_voucher=True) + doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True) doc.make_gl_entries() def update_rate_in_serial_no(self, receipt_document): diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 3cf347e9473..cf28e50e2d2 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -222,7 +222,13 @@ frappe.ui.form.on('Material Request', { fieldname:'default_supplier', fieldtype: 'Link', options: 'Supplier', - description: __('Selected Supplier must be the Default Supplier of one of the items below.'), + description: __('Select a Supplier from the Default Supplier List of the items below.'), + get_query: () => { + return { + query: "erpnext.stock.doctype.material_request.material_request.get_default_supplier_query", + filters: {'doc': frm.doc.name} + } + } }, (values) => { frappe.model.open_mapped_doc({ diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 44b711b865e..eb76e1f7a38 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -371,6 +371,18 @@ def get_material_requests_based_on_supplier(supplier): material_requests = [] return material_requests, supplier_items +def get_default_supplier_query(doctype, txt, searchfield, start, page_len, filters): + doc = frappe.get_doc("Material Request", filters.get("doc")) + item_list = [] + for d in doc.items: + item_list.append(d.item_code) + + return frappe.db.sql("""select default_supplier + from `tabItem Default` + where parent in ({0}) and + default_supplier IS NOT NULL + """.format(', '.join(['%s']*len(item_list))),tuple(item_list)) + @frappe.whitelist() def make_supplier_quotation(source_name, target_doc=None): def postprocess(source, target_doc): diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.js b/erpnext/stock/doctype/stock_settings/stock_settings.js index 49ce3d8ef7a..cc0e2cfc425 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.js +++ b/erpnext/stock/doctype/stock_settings/stock_settings.js @@ -3,6 +3,15 @@ frappe.ui.form.on('Stock Settings', { refresh: function(frm) { + let filters = function() { + return { + filters : { + is_group : 0 + } + }; + }; + frm.set_query("default_warehouse", filters); + frm.set_query("sample_retention_warehouse", filters); } }); diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py index 65de2e58d3a..93b5eee75c2 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.py +++ b/erpnext/stock/doctype/stock_settings/stock_settings.py @@ -30,9 +30,17 @@ class StockSettings(Document): frappe.make_property_setter({'fieldname': name, 'property': 'hidden', 'value': 0 if self.show_barcode_field else 1}) + self.validate_warehouses() self.cant_change_valuation_method() self.validate_clean_description_html() + def validate_warehouses(self): + warehouse_fields = ["default_warehouse", "sample_retention_warehouse"] + for field in warehouse_fields: + if frappe.db.get_value("Warehouse", self.get(field), "is_group"): + frappe.throw(_("Group Warehouses cannot be used in transactions. Please change the value of {0}") \ + .format(frappe.bold(self.meta.get_field(field).label)), title =_("Incorrect Warehouse")) + def cant_change_valuation_method(self): db_valuation_method = frappe.db.get_single_value("Stock Settings", "valuation_method")