diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 85e34036fea..282d6e40e7c 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.20' +__version__ = '10.1.21' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 44658f71952..78dcee9fb8f 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -336,7 +336,7 @@ class SalesInvoice(SellingController): for item in self.get("items"): if item.get('item_code'): for fname, val in get_pos_profile_item_details(pos, - frappe._dict(item.as_dict()), pos).items(): + frappe._dict(item.as_dict()), pos, True).items(): if (not for_validate) or (for_validate and not item.get(fname)): item.set(fname, val) diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index 4aadd76aa43..a9b1813c4d0 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -20,6 +20,10 @@ frappe.ui.form.on("Opportunity", { frm.trigger('set_contact_link'); }, + with_items: function(frm) { + frm.trigger('toggle_mandatory'); + }, + customer_address: function(frm, cdt, cdn) { erpnext.utils.get_address_display(frm, 'customer_address', 'address_display', false); }, @@ -35,6 +39,7 @@ frappe.ui.form.on("Opportunity", { var doc = frm.doc; frm.events.enquiry_from(frm); frm.trigger('set_contact_link'); + frm.trigger('toggle_mandatory'); erpnext.toggle_naming_series(); if(!doc.__islocal && doc.status!=="Lost") { @@ -82,6 +87,10 @@ frappe.ui.form.on("Opportunity", { method: "erpnext.crm.doctype.opportunity.opportunity.make_supplier_quotation", frm: cur_frm }) + }, + + toggle_mandatory: function(frm) { + frm.toggle_reqd("items", frm.doc.with_items ? 1:0); } }) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index eb739466d69..1a40e7d89bc 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -358,7 +358,7 @@ def get_leave_allocation_records(date, employee=None): return allocated_leaves - +@frappe.whitelist() def get_holidays(employee, from_date, to_date): '''get holidays between two dates for the given employee''' holiday_list = get_holiday_list_for_employee(employee) diff --git a/erpnext/patches/v9_2/rename_translated_domains_in_en.py b/erpnext/patches/v9_2/rename_translated_domains_in_en.py index c787b842392..fc3d01cc608 100644 --- a/erpnext/patches/v9_2/rename_translated_domains_in_en.py +++ b/erpnext/patches/v9_2/rename_translated_domains_in_en.py @@ -2,6 +2,7 @@ import frappe from frappe import _ def execute(): + frappe.reload_doc('stock', 'doctype', 'item') language = frappe.get_single("System Settings").language if language and language.startswith('en'): return diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index fb2fabace40..b878a1ea5d0 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -10,7 +10,7 @@ def validate_gstin_for_india(doc, method): if doc.gstin: doc.gstin = doc.gstin.upper() - if doc.gstin != "NA": + if doc.gstin not in ["NA", "na"]: p = re.compile("[0-9]{2}[a-zA-Z]{5}[0-9]{4}[a-zA-Z]{1}[1-9A-Za-z]{1}[Z]{1}[0-9a-zA-Z]{1}") if not p.match(doc.gstin): frappe.throw(_("Invalid GSTIN or Enter NA for Unregistered")) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index fc9254b2a3a..20685deaac0 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -65,6 +65,10 @@ frappe.ui.form.on("Sales Order Item", { }); erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({ + onload: function(doc, dt, dn) { + this._super(); + }, + refresh: function(doc, dt, dn) { var me = this; this._super(); diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index fe13432a810..635e937289e 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -52,6 +52,7 @@ def get_items(start, page_length, price_list, item_group, search_value="", pos_p where i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1 and i.item_group in (select name from `tabItem Group` where lft >= {lft} and rgt <= {rgt}) + and ifnull(i.end_of_life, curdate()) >= curdate() and {condition} limit {start}, {page_length}""".format(start=start, page_length=page_length, lft=lft, rgt=rgt, condition=condition), diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index a6137f9dbdf..caa44ac1f6a 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -140,7 +140,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 0, + "search_index": 1, "set_only_once": 1, "translatable": 0, "unique": 0 @@ -236,7 +236,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -3188,7 +3188,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, - "search_index": 1, + "search_index": 0, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -3717,7 +3717,7 @@ "issingle": 0, "istable": 0, "max_attachments": 1, - "modified": "2018-03-12 03:13:18.516087", + "modified": "2018-04-11 12:21:48.715529", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 552d284b238..7e456dd3d37 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -411,7 +411,7 @@ def get_party_item_code(args, item_doc, out): item_supplier = item_doc.get("supplier_items", {"supplier": args.supplier}) out.supplier_part_no = item_supplier[0].supplier_part_no if item_supplier else None -def get_pos_profile_item_details(company, args, pos_profile=None): +def get_pos_profile_item_details(company, args, pos_profile=None, update_data=False): res = frappe._dict() if not pos_profile: @@ -419,7 +419,7 @@ def get_pos_profile_item_details(company, args, pos_profile=None): if pos_profile: for fieldname in ("income_account", "cost_center", "warehouse", "expense_account"): - if not args.get(fieldname) and pos_profile.get(fieldname): + if (not args.get(fieldname) or update_data) and pos_profile.get(fieldname): res[fieldname] = pos_profile.get(fieldname) if res.get("warehouse"): diff --git a/erpnext/stock/report/stock_balance/stock_balance.js b/erpnext/stock/report/stock_balance/stock_balance.js index 4aa240d8dc4..90945e9f91e 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.js +++ b/erpnext/stock/report/stock_balance/stock_balance.js @@ -9,7 +9,7 @@ frappe.query_reports["Stock Balance"] = { "fieldtype": "Date", "width": "80", "reqd": 1, - "default": frappe.sys_defaults.year_start_date, + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), }, { "fieldname":"to_date", @@ -31,7 +31,12 @@ frappe.query_reports["Stock Balance"] = { "label": __("Item"), "fieldtype": "Link", "width": "80", - "options": "Item" + "options": "Item", + "get_query": function() { + return { + query: "erpnext.controllers.queries.item_query" + } + } }, { "fieldname": "warehouse", diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 7997c37cbfa..83e06dc5328 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -4,7 +4,8 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import flt, cint, getdate +from frappe.utils import flt, cint, getdate, now +from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition from six import iteritems @@ -14,10 +15,11 @@ def execute(filters=None): validate_filters(filters) columns = get_columns() - item_map = get_item_details(filters) - item_reorder_detail_map = get_item_reorder_details(filters) - iwb_map = get_item_warehouse_map(filters) - + items = get_items(filters) + sle = get_stock_ledger_entries(filters, items) + iwb_map = get_item_warehouse_map(filters, sle) + item_map = get_item_details(items, sle, filters) + item_reorder_detail_map = get_item_reorder_details(item_map.keys()) data = [] for (company, item, warehouse) in sorted(iwb_map): @@ -90,21 +92,9 @@ def get_conditions(filters): else: frappe.throw(_("'To Date' is required")) - if filters.get("item_group"): - ig_details = frappe.db.get_value("Item Group", filters.get("item_group"), - ["lft", "rgt"], as_dict=1) - - if ig_details: - conditions += """ - and exists (select name from `tabItem Group` ig - where ig.lft >= %s and ig.rgt <= %s and item.item_group = ig.name) - """ % (ig_details.lft, ig_details.rgt) - - if filters.get("item_code"): - conditions += " and sle.item_code = '%s'" % frappe.db.escape(filters.get("item_code"), percent=False) - if filters.get("warehouse"): - warehouse_details = frappe.db.get_value("Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1) + warehouse_details = frappe.db.get_value("Warehouse", + filters.get("warehouse"), ["lft", "rgt"], as_dict=1) if warehouse_details: conditions += " and exists (select name from `tabWarehouse` wh \ where wh.lft >= %s and wh.rgt <= %s and sle.warehouse = wh.name)"%(warehouse_details.lft, @@ -112,30 +102,29 @@ def get_conditions(filters): return conditions -def get_stock_ledger_entries(filters): +def get_stock_ledger_entries(filters, items): + item_conditions_sql = '' + if items: + item_conditions_sql = ' and sle.item_code in ({})'\ + .format(', '.join(['"' + frappe.db.escape(i) + '"' for i in items])) + conditions = get_conditions(filters) - - join_table_query = "" - if filters.get("item_group"): - join_table_query = "inner join `tabItem` item on item.name = sle.item_code" - + return frappe.db.sql(""" select sle.item_code, warehouse, sle.posting_date, sle.actual_qty, sle.valuation_rate, sle.company, sle.voucher_type, sle.qty_after_transaction, sle.stock_value_difference from - `tabStock Ledger Entry` sle force index (posting_sort_index) %s - where sle.docstatus < 2 %s + `tabStock Ledger Entry` sle force index (posting_sort_index) + where sle.docstatus < 2 %s %s order by sle.posting_date, sle.posting_time, sle.name""" % - (join_table_query, conditions), as_dict=1) + (item_conditions_sql, conditions), as_dict=1) -def get_item_warehouse_map(filters): +def get_item_warehouse_map(filters, sle): iwb_map = {} from_date = getdate(filters.get("from_date")) to_date = getdate(filters.get("to_date")) - sle = get_stock_ledger_entries(filters) - for d in sle: key = (d.company, d.item_code, d.warehouse) if key not in iwb_map: @@ -193,20 +182,33 @@ def filter_items_with_no_transactions(iwb_map): return iwb_map -def get_item_details(filters): - condition = '' - value = () +def get_items(filters): + conditions = [] if filters.get("item_code"): - condition = "where item_code=%s" - value = (filters.get("item_code"),) + conditions.append("item.name=%(item_code)s") + else: + if filters.get("brand"): + conditions.append("item.brand=%(brand)s") + if filters.get("item_group"): + conditions.append(get_item_group_condition(filters.get("item_group"))) - items = frappe.db.sql(""" - select name, item_name, stock_uom, item_group, brand, description - from tabItem - {condition} - """.format(condition=condition), value, as_dict=1) + items = [] + if conditions: + items = frappe.db.sql_list("""select name from `tabItem` item where {}""" + .format(" and ".join(conditions)), filters) + return items - item_details = dict((d.name , d) for d in items) +def get_item_details(items, sle, filters): + item_details = {} + if not items: + items = list(set([d.item_code for d in sle])) + + for item in frappe.db.sql(""" + select name, item_name, description, item_group, brand, stock_uom + from `tabItem` + where name in ({0}) + """.format(', '.join(['"' + frappe.db.escape(i) + '"' for i in items])), as_dict=1): + item_details.setdefault(item.name, item) if filters.get('show_variant_attributes', 0) == 1: variant_values = get_variant_values_for(item_details.keys()) @@ -214,18 +216,12 @@ def get_item_details(filters): return item_details -def get_item_reorder_details(filters): - condition = '' - value = () - if filters.get("item_code"): - condition = "where parent=%s" - value = (filters.get("item_code"),) - +def get_item_reorder_details(items): item_reorder_details = frappe.db.sql(""" select parent, warehouse, warehouse_reorder_qty, warehouse_reorder_level from `tabItem Reorder` - {condition} - """.format(condition=condition), value, as_dict=1) + where parent in ({0}) + """.format(', '.join(['"' + frappe.db.escape(i) + '"' for i in items])), as_dict=1) return dict((d.parent + d.warehouse, d) for d in item_reorder_details) @@ -235,12 +231,10 @@ def validate_filters(filters): if sle_count > 500000: frappe.throw(_("Please set filter based on Item or Warehouse")) - def get_variants_attributes(): '''Return all item variant attributes.''' return [i.name for i in frappe.get_all('Item Attribute')] - def get_variant_values_for(items): '''Returns variant values for items.''' attribute_map = {} diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.js b/erpnext/stock/report/stock_ledger/stock_ledger.js index d4f5ab5414f..660357cdc32 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.js +++ b/erpnext/stock/report/stock_ledger/stock_ledger.js @@ -35,7 +35,12 @@ frappe.query_reports["Stock Ledger"] = { "fieldname":"item_code", "label": __("Item"), "fieldtype": "Link", - "options": "Item" + "options": "Item", + "get_query": function() { + return { + query: "erpnext.controllers.queries.item_query" + } + } }, { "fieldname":"item_group", diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index ef198f0d23c..c9286a3e87a 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -7,13 +7,12 @@ from frappe import _ def execute(filters=None): columns = get_columns() - item_conditions = get_item_conditions(filters) - item_details = get_item_details(filters, item_conditions) - sl_entries = get_stock_ledger_entries(filters, item_conditions, item_details) + items = get_items(filters) + sl_entries = get_stock_ledger_entries(filters, items) + item_details = get_item_details(items, sl_entries) opening_row = get_opening_balance(filters, columns) data = [] - if opening_row: data.append(opening_row) @@ -53,12 +52,12 @@ def get_columns(): return columns -def get_stock_ledger_entries(filters, item_conditions, item_details): +def get_stock_ledger_entries(filters, items): item_conditions_sql = '' - if item_conditions: - items = ['"' + frappe.db.escape(i) + '"' for i in item_details.keys()] - if items: - item_conditions_sql = 'and sle.item_code in ({})'.format(', '.join(items)) + if items: + item_conditions_sql = 'and sle.item_code in ({})'\ + .format(', '.join(['"' + frappe.db.escape(i) + '"' for i in items])) + return frappe.db.sql("""select concat_ws(" ", posting_date, posting_time) as date, item_code, warehouse, actual_qty, qty_after_transaction, incoming_rate, valuation_rate, stock_value, voucher_type, voucher_no, batch_no, serial_no, company, project @@ -73,25 +72,35 @@ def get_stock_ledger_entries(filters, item_conditions, item_details): item_conditions_sql = item_conditions_sql ), filters, as_dict=1) -def get_item_details(filters, item_conditions): - item_details = {} - for item in frappe.db.sql("""select name, item_name, description, item_group, - brand, stock_uom from `tabItem` item {item_conditions}"""\ - .format(item_conditions=item_conditions), filters, as_dict=1): - item_details.setdefault(item.name, item) - - return item_details - -def get_item_conditions(filters): +def get_items(filters): conditions = [] if filters.get("item_code"): conditions.append("item.name=%(item_code)s") - if filters.get("brand"): - conditions.append("item.brand=%(brand)s") - if filters.get("item_group"): - conditions.append(get_item_group_condition(filters.get("item_group"))) + else: + if filters.get("brand"): + conditions.append("item.brand=%(brand)s") + if filters.get("item_group"): + conditions.append(get_item_group_condition(filters.get("item_group"))) - return "where {}".format(" and ".join(conditions)) if conditions else "" + items = [] + if conditions: + items = frappe.db.sql_list("""select name from `tabItem` item where {}""" + .format(" and ".join(conditions)), filters) + return items + +def get_item_details(items, sl_entries): + item_details = {} + if not items: + items = list(set([d.item_code for d in sl_entries])) + + for item in frappe.db.sql(""" + select name, item_name, description, item_group, brand, stock_uom + from `tabItem` + where name in ({0}) + """.format(', '.join(['"' + frappe.db.escape(i) + '"' for i in items])), as_dict=1): + item_details.setdefault(item.name, item) + + return item_details def get_sle_conditions(filters): conditions = [] diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js index 2dbbc5b21c9..937c0a2572f 100644 --- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js +++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js @@ -19,7 +19,12 @@ frappe.query_reports["Stock Projected Qty"] = { "fieldname":"item_code", "label": __("Item"), "fieldtype": "Link", - "options": "Item" + "options": "Item", + "get_query": function() { + return { + query: "erpnext.controllers.queries.item_query" + } + } }, { "fieldname":"brand",