diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6503508c4e9..ba42d34e23a 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__ = '10.1.36' +__version__ = '10.1.37' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 4b7b43cc199..48b82bde40e 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -35,6 +35,7 @@ class BuyingController(StockController): if getattr(self, "supplier", None) and not self.supplier_name: self.supplier_name = frappe.db.get_value("Supplier", self.supplier, "supplier_name") + self.validate_items() self.set_qty_as_per_stock_uom() self.validate_stock_or_nonstock_items() self.validate_warehouse() @@ -456,3 +457,31 @@ class BuyingController(StockController): else: frappe.throw(_("Please enter Reqd by Date")) + def validate_items(self): + # validate items to see if they have is_purchase_item or is_subcontracted_item enabled + + if hasattr(self, "is_subcontracted") and self.is_subcontracted == 'Yes': + validate_item_type(self, "is_sub_contracted_item", "subcontracted") + else: + validate_item_type(self, "is_purchase_item", "purchase") + +def validate_item_type(doc, fieldname, message): + # iterate through items and check if they are valid sales or purchase items + items = [d.item_code for d in doc.items if d.item_code] + + # No validation check inase of creating transaction using 'Opening Invoice Creation Tool' + if not items: + return + + item_list = ", ".join(["'%s'" % frappe.db.escape(d) for d in items]) + + invalid_items = [d[0] for d in frappe.db.sql(""" + select item_code from tabItem where name in ({0}) and {1}=0 + """.format(item_list, fieldname), as_list=True)] + + if invalid_items: + frappe.throw(_("Following item {items} {verb} not marked as {message} item.\ + You can enable them as {message} item from its Item master".format( + items = ", ".join([d for d in invalid_items]), + verb = "are" if len(invalid_items) > 1 else "is", + message = message))) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 2a22b32637b..fcc9d75e79c 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -35,6 +35,7 @@ class SellingController(StockController): def validate(self): super(SellingController, self).validate() + self.validate_items() self.validate_max_discount() self.validate_selling_price() self.set_qty_as_per_stock_uom() @@ -337,6 +338,11 @@ class SellingController(StockController): po_nos = frappe.get_all('Sales Order', 'po_no', filters = {'name': ('in', sales_orders)}) self.po_no = ', '.join(list(set([d.po_no for d in po_nos if d.po_no]))) + def validate_items(self): + # validate items to see if they have is_sales_item enabled + from erpnext.controllers.buying_controller import validate_item_type + validate_item_type(self, "is_sales_item", "sales") + def check_active_sales_items(obj): for d in obj.get("items"): if d.item_code: diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index d0c4a46e3ec..f674a61d918 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -45,8 +45,9 @@ class ExpenseClaim(AccountsController): }[cstr(self.docstatus or 0)] paid_amount = flt(self.total_amount_reimbursed) + flt(self.total_advance_amount) + precision = self.precision("total_sanctioned_amount") if (self.is_paid or (flt(self.total_sanctioned_amount) > 0 - and flt(self.total_sanctioned_amount) == paid_amount)) \ + and flt(self.total_sanctioned_amount, precision) == flt(paid_amount, precision))) \ and self.docstatus == 1 and self.approval_status == 'Approved': self.status = "Paid" elif flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1 and self.approval_status == 'Approved': diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index 75d5ce9e219..0f28d4e3ba5 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -169,21 +169,25 @@ erpnext.utils.validate_mandatory = function(frm, label, value, trigger_on) { } erpnext.utils.get_shipping_address = function(frm, callback){ - frappe.call({ - method: "frappe.contacts.doctype.address.address.get_shipping_address", - args: { - company: frm.doc.company, - address: frm.doc.shipping_address - }, - callback: function(r){ - if(r.message){ - frm.set_value("shipping_address", r.message[0]) //Address title or name - frm.set_value("shipping_address_display", r.message[1]) //Address to be displayed on the page - } + if (frm.doc.company) { + frappe.call({ + method: "frappe.contacts.doctype.address.address.get_shipping_address", + args: { + company: frm.doc.company, + address: frm.doc.shipping_address + }, + callback: function(r){ + if(r.message){ + frm.set_value("shipping_address", r.message[0]) //Address title or name + frm.set_value("shipping_address_display", r.message[1]) //Address to be displayed on the page + } - if(callback){ - return callback(); + if(callback){ + return callback(); + } } - } - }); + }); + } else { + frappe.msgprint(__("Select company first")); + } } \ No newline at end of file diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 7232fadb56e..b17345c2e75 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -79,7 +79,7 @@ class Customer(TransactionBase): def update_customer_groups(self): ignore_doctypes = ["Lead", "Opportunity", "POS Profile", "Tax Rule", "Pricing Rule"] if frappe.flags.customer_group_changed: - update_linked_doctypes('Customer', self.name, 'Customer Group', + update_linked_doctypes('Customer', frappe.db.escape(self.name), 'Customer Group', self.customer_group, ignore_doctypes) def create_primary_contact(self): diff --git a/erpnext/setup/doctype/authorization_control/authorization_control.py b/erpnext/setup/doctype/authorization_control/authorization_control.py index 7d5e80b8f23..2240e0c28b5 100644 --- a/erpnext/setup/doctype/authorization_control/authorization_control.py +++ b/erpnext/setup/doctype/authorization_control/authorization_control.py @@ -40,7 +40,7 @@ class AuthorizationControl(TransactionBase): chk = 1 add_cond1,add_cond2 = '','' if based_on == 'Itemwise Discount': - add_cond1 += " and master_name = '"+cstr(item).replace("'", "\\'")+"'" + add_cond1 += " and master_name = '"+cstr(frappe.db.escape(item)).replace("'", "\\'")+"'" itemwise_exists = frappe.db.sql("""select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and company = %s and docstatus != 2 %s %s""" % diff --git a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py index 6d5848ad78f..c488b996ffa 100644 --- a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py +++ b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py @@ -2,6 +2,7 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals import frappe, unittest +from frappe.utils import flt from erpnext.setup.utils import get_exchange_rate test_records = frappe.get_test_records('Currency Exchange') @@ -44,7 +45,7 @@ class TestCurrencyExchange(unittest.TestCase): # Start with allow_stale is True exchange_rate = get_exchange_rate("USD", "INR", "2016-01-01") - self.assertEqual(exchange_rate, 60.0) + self.assertEqual(flt(exchange_rate, 3), 60.0) exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15") self.assertEqual(exchange_rate, 65.1) @@ -56,7 +57,7 @@ class TestCurrencyExchange(unittest.TestCase): self.clear_cache() exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15") self.assertFalse(exchange_rate == 60) - self.assertEqual(exchange_rate, 66.894) + self.assertEqual(flt(exchange_rate, 3), 66.894) def test_exchange_rate_strict(self): # strict currency settings @@ -69,7 +70,7 @@ class TestCurrencyExchange(unittest.TestCase): # Will fetch from fixer.io self.clear_cache() exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15") - self.assertEqual(exchange_rate, 67.79) + self.assertEqual(flt(exchange_rate, 3), 67.79) exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30") self.assertEqual(exchange_rate, 62.9) @@ -77,7 +78,7 @@ class TestCurrencyExchange(unittest.TestCase): # Exchange rate as on 15th Dec, 2015, should be fetched from fixer.io self.clear_cache() exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15") - self.assertEqual(exchange_rate, 66.894) + self.assertEqual(flt(exchange_rate, 3), 66.894) exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-10") self.assertEqual(exchange_rate, 65.1) @@ -100,4 +101,4 @@ class TestCurrencyExchange(unittest.TestCase): # Will fetch from fixer.io self.clear_cache() exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15") - self.assertEqual(exchange_rate, 67.79) \ No newline at end of file + self.assertEqual(flt(exchange_rate, 3), 67.79) \ No newline at end of file diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index 5fcc3a2c68f..4f62815f90f 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -95,7 +95,7 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None): if not value: import requests - api_url = "http://api.fixer.io/{0}".format(transaction_date) + api_url = "https://exchangeratesapi.io/api/{0}".format(transaction_date) response = requests.get(api_url, params={ "base": from_currency, "symbols": to_currency diff --git a/erpnext/stock/doctype/packing_slip_item/packing_slip_item.json b/erpnext/stock/doctype/packing_slip_item/packing_slip_item.json index 009244ce5e0..d240b7d8405 100644 --- a/erpnext/stock/doctype/packing_slip_item/packing_slip_item.json +++ b/erpnext/stock/doctype/packing_slip_item/packing_slip_item.json @@ -88,6 +88,7 @@ "label": "Item Name", "length": 0, "no_copy": 0, + "options": "item_code.item_name", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -435,7 +436,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-02-20 13:30:26.818408", + "modified": "2018-06-01 07:21:58.220980", "modified_by": "Administrator", "module": "Stock", "name": "Packing Slip Item",