From c14f1f145b7e30b71104c96498c6fb708511b95f Mon Sep 17 00:00:00 2001 From: tundebabzy Date: Sat, 27 Jan 2018 06:28:29 +0100 Subject: [PATCH 01/18] adjust stock_qty for expired quantities adjust based on warehouse add parameters to `get_qty_in_stock` so it can be useful in other parts of the code base --- .../setup/doctype/item_group/item_group.py | 22 +++++++- erpnext/utilities/product.py | 50 +++++++++++++++++-- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index 39172d7d11..bb1910720d 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -9,6 +9,7 @@ from frappe.utils.nestedset import NestedSet from frappe.website.website_generator import WebsiteGenerator from frappe.website.render import clear_cache from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow +from erpnext.utilities.product import get_qty_in_stock class ItemGroup(NestedSet, WebsiteGenerator): @@ -83,7 +84,8 @@ def get_product_list_for_group(product_group=None, start=0, limit=10, search=Non # base query query = """select I.name, I.item_name, I.item_code, I.route, I.image, I.website_image, I.thumbnail, I.item_group, I.description, I.web_long_description as website_description, I.is_stock_item, - case when (S.actual_qty - S.reserved_qty) > 0 then 1 else 0 end as in_stock + case when (S.actual_qty - S.reserved_qty) > 0 then 1 else 0 end as in_stock, I.website_warehouse, + I.has_batch_no from `tabItem` I left join tabBin S on I.item_code = S.item_code and I.website_warehouse = S.warehouse where I.show_in_website = 1 @@ -104,8 +106,26 @@ def get_product_list_for_group(product_group=None, start=0, limit=10, search=Non data = frappe.db.sql(query, {"product_group": product_group,"search": search, "today": nowdate()}, as_dict=1) + data = adjust_for_expired_items(data) + return [get_item_for_list_in_html(r) for r in data] + +def adjust_for_expired_items(data): + adjusted_data = [] + + for item in data: + if item.get('has_batch_no') and item.get('website_warehouse'): + stock_qty_dict = get_qty_in_stock( + item.get('name'), 'website_warehouse', item.get('website_warehouse')) + qty = stock_qty_dict.stock_qty[0][0] + item['in_stock'] = 1 if qty else 0 + adjusted_data.append(item) + + return adjusted_data + + + def get_child_groups(item_group_name): item_group = frappe.get_doc("Item Group", item_group_name) return frappe.db.sql("""select name diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py index 9d5c056ba7..f088e3267d 100644 --- a/erpnext/utilities/product.py +++ b/erpnext/utilities/product.py @@ -4,14 +4,17 @@ from __future__ import unicode_literals import frappe -from frappe.utils import cint, fmt_money, flt +from frappe.utils import cint, fmt_money, flt, nowdate, getdate from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item +from erpnext.stock.doctype.batch.batch import get_batch_qty -def get_qty_in_stock(item_code, item_warehouse_field): +def get_qty_in_stock(item_code, item_warehouse_field, warehouse=None): in_stock, stock_qty = 0, '' template_item_code, is_stock_item = frappe.db.get_value("Item", item_code, ["variant_of", "is_stock_item"]) - warehouse = frappe.db.get_value("Item", item_code, item_warehouse_field) + if not warehouse: + warehouse = frappe.db.get_value("Item", item_code, item_warehouse_field) + if not warehouse and template_item_code and template_item_code != item_code: warehouse = frappe.db.get_value("Item", template_item_code, item_warehouse_field) @@ -21,8 +24,49 @@ def get_qty_in_stock(item_code, item_warehouse_field): if stock_qty: in_stock = stock_qty[0][0] > 0 and 1 or 0 + if stock_qty[0][0]: + stock_qty = adjust_for_expired_items(item_code, stock_qty, warehouse) + + if stock_qty[0][0]: + in_stock = stock_qty[0][0] > 0 and 1 or 0 + return frappe._dict({"in_stock": in_stock, "stock_qty": stock_qty, "is_stock_item": is_stock_item}) + +def adjust_for_expired_items(item_code, stock_qty, warehouse): + batches = frappe.get_list('Batch', filters=[{'item': item_code}], fields=['expiry_date', 'name']) + expired_batches = get_expired_batches(batches) + stock_qty = [list(item) for item in stock_qty] + + if expired_batches: + for batch in expired_batches: + if warehouse: + stock_qty[0][0] = max(0, stock_qty[0][0] - get_batch_qty(batch, warehouse)) + else: + stock_qty[0][0] = max(0, stock_qty[0][0] - qty_from_all_warehouses(get_batch_qty(batch))) + + if not stock_qty[0][0]: + break + return stock_qty + + +def get_expired_batches(batches): + """ + :param batches: A list of dict in the form [{'expiry_date': datetime.date(20XX, 1, 1), 'name': 'batch_id'}, ...] + """ + return [b.name for b in batches if b.expiry_date and b.expiry_date <= getdate(nowdate())] + + +def qty_from_all_warehouses(batch_info): + """ + :param batch_info: A list of dict in the form [{u'warehouse': u'Stores - I', u'qty': 0.8}, ...] + """ + qty = 0 + for batch in batch_info: + qty = qty + batch.qty + + return qty + def get_price(item_code, price_list, customer_group, company, qty=1): template_item_code = frappe.db.get_value("Item", item_code, "variant_of") From c7c1defe648310bb1971735f71a8fc9e5a24aa75 Mon Sep 17 00:00:00 2001 From: tundebabzy Date: Sat, 27 Jan 2018 06:30:56 +0100 Subject: [PATCH 02/18] after adjusting stock_qty for expired, set in_stock flag --- erpnext/utilities/product.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py index f088e3267d..5a9322bb38 100644 --- a/erpnext/utilities/product.py +++ b/erpnext/utilities/product.py @@ -21,8 +21,6 @@ def get_qty_in_stock(item_code, item_warehouse_field, warehouse=None): if warehouse: stock_qty = frappe.db.sql("""select GREATEST(actual_qty - reserved_qty, 0) from tabBin where item_code=%s and warehouse=%s""", (item_code, warehouse)) - if stock_qty: - in_stock = stock_qty[0][0] > 0 and 1 or 0 if stock_qty[0][0]: stock_qty = adjust_for_expired_items(item_code, stock_qty, warehouse) @@ -30,6 +28,9 @@ def get_qty_in_stock(item_code, item_warehouse_field, warehouse=None): if stock_qty[0][0]: in_stock = stock_qty[0][0] > 0 and 1 or 0 + if stock_qty: + in_stock = stock_qty[0][0] > 0 and 1 or 0 + return frappe._dict({"in_stock": in_stock, "stock_qty": stock_qty, "is_stock_item": is_stock_item}) From 4dc329f5ea716cc70b52a96db0a3a032696f7431 Mon Sep 17 00:00:00 2001 From: Vishal Date: Wed, 24 Jan 2018 16:46:18 +0530 Subject: [PATCH 03/18] [fix] Sales order link to purchase order not working fixed --- erpnext/stock/doctype/material_request/material_request.py | 5 +++-- erpnext/stock/get_item_details.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index da310aa3d7..c4059c1848 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -65,7 +65,7 @@ class MaterialRequest(BuyingController): self.status = "Draft" from erpnext.controllers.status_updater import validate_status - validate_status(self.status, + validate_status(self.status, ["Draft", "Submitted", "Stopped", "Cancelled", "Pending", "Partially Ordered", "Ordered", "Issued", "Transferred"]) @@ -240,7 +240,8 @@ def make_purchase_order(source_name, target_doc=None): ["name", "material_request_item"], ["parent", "material_request"], ["uom", "stock_uom"], - ["uom", "uom"] + ["uom", "uom"], + ["sales_order", "sales_order"] ], "postprocess": update_item, "condition": lambda doc: doc.ordered_qty < doc.stock_qty diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index a7638b4169..6b6723331a 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -472,8 +472,8 @@ def get_projected_qty(item_code, warehouse): @frappe.whitelist() def get_bin_details(item_code, warehouse): return frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, - ["projected_qty", "actual_qty"], as_dict=True) \ - or {"projected_qty": 0, "actual_qty": 0} + ["projected_qty", "actual_qty", "ordered_qty"], as_dict=True) \ + or {"projected_qty": 0, "actual_qty": 0, "ordered_qty": 0} @frappe.whitelist() def get_serial_no_details(item_code, warehouse, stock_qty, serial_no): From fc05cc4e70911444563f11910a0ef31c4dcd31f1 Mon Sep 17 00:00:00 2001 From: Vishal Date: Thu, 25 Jan 2018 15:27:35 +0530 Subject: [PATCH 04/18] [fix] link with supplier quotation to purchase order --- .../supplier_quotation/supplier_quotation.py | 3 +- .../supplier_quotation_item.json | 33 ++++++++++++++++++- .../material_request/material_request.py | 3 +- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py index c395d0cfd7..b221a08959 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py @@ -124,7 +124,8 @@ def make_purchase_order(source_name, target_doc=None): ["name", "supplier_quotation_item"], ["parent", "supplier_quotation"], ["material_request", "material_request"], - ["material_request_item", "material_request_item"] + ["material_request_item", "material_request_item"], + ["sales_order", "sales_order"] ], "postprocess": update_item }, diff --git a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json index b3a55b645d..da78c12ba0 100644 --- a/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json +++ b/erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json @@ -1326,6 +1326,37 @@ "unique": 0, "width": "120px" }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "sales_order", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Sales Order", + "length": 0, + "no_copy": 0, + "options": "Sales Order", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 1, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -1614,7 +1645,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-12-14 09:37:47.427897", + "modified": "2018-01-25 15:04:40.171617", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation Item", diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index c4059c1848..defce62e2e 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -345,7 +345,8 @@ def make_supplier_quotation(source_name, target_doc=None): "doctype": "Supplier Quotation Item", "field_map": { "name": "material_request_item", - "parent": "material_request" + "parent": "material_request", + "sales_order": "sales_order" } } }, target_doc, postprocess) From df1653827fe5187ed99e2f230929517d2b2a7d6d Mon Sep 17 00:00:00 2001 From: vishdha Date: Wed, 31 Jan 2018 12:38:46 +0530 Subject: [PATCH 05/18] [fix] Patch for material request to purchase order added --- erpnext/patches.txt | 3 ++- ...pdate_sales_order_link_to_purchase_order.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v10_0/update_sales_order_link_to_purchase_order.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index dbd098d119..9474a946e8 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -486,4 +486,5 @@ erpnext.patches.v10_0.add_guardian_role_for_parent_portal erpnext.patches.v10_0.set_numeric_ranges_in_template_if_blank erpnext.patches.v10_0.update_assessment_plan erpnext.patches.v10_0.update_assessment_result -erpnext.patches.v10_0.set_default_payment_terms_based_on_company \ No newline at end of file +erpnext.patches.v10_0.set_default_payment_terms_based_on_company +erpnext.patches.v10_0.update_sales_order_link_to_purchase_order \ No newline at end of file diff --git a/erpnext/patches/v10_0/update_sales_order_link_to_purchase_order.py b/erpnext/patches/v10_0/update_sales_order_link_to_purchase_order.py new file mode 100644 index 0000000000..b4f58384bf --- /dev/null +++ b/erpnext/patches/v10_0/update_sales_order_link_to_purchase_order.py @@ -0,0 +1,18 @@ +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc("buying", "doctype", "supplier_quotation_item") + + for doctype in ['Purchase Order','Supplier Quotation']: + frappe.db.sql(""" + Update + `tab{doctype} Item`, `tabMaterial Request Item` + set + `tab{doctype} Item`.sales_order = `tabMaterial Request Item`.sales_order + where + `tab{doctype} Item`.material_request= `tabMaterial Request Item`.parent + and `tab{doctype} Item`.material_request_item = `tabMaterial Request Item`.name + and `tabMaterial Request Item`.sales_order is not null""".format(doctype=doctype)) \ No newline at end of file From 29c8142678d9f81a7098266bdff0e762360f9bab Mon Sep 17 00:00:00 2001 From: tundebabzy Date: Wed, 31 Jan 2018 10:36:31 +0100 Subject: [PATCH 06/18] refactor `adjust_for_expired_items` and others as per code review use get_all instead of get_list rename `adjust_for_expired_items` to `adjust_qty_for_expired_items` --- .../setup/doctype/item_group/item_group.py | 4 ++-- erpnext/utilities/product.py | 22 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index bb1910720d..14a478d7e4 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -106,12 +106,12 @@ def get_product_list_for_group(product_group=None, start=0, limit=10, search=Non data = frappe.db.sql(query, {"product_group": product_group,"search": search, "today": nowdate()}, as_dict=1) - data = adjust_for_expired_items(data) + data = adjust_qty_for_expired_items(data) return [get_item_for_list_in_html(r) for r in data] -def adjust_for_expired_items(data): +def adjust_qty_for_expired_items(data): adjusted_data = [] for item in data: diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py index 5a9322bb38..5901b6031d 100644 --- a/erpnext/utilities/product.py +++ b/erpnext/utilities/product.py @@ -23,7 +23,7 @@ def get_qty_in_stock(item_code, item_warehouse_field, warehouse=None): item_code=%s and warehouse=%s""", (item_code, warehouse)) if stock_qty[0][0]: - stock_qty = adjust_for_expired_items(item_code, stock_qty, warehouse) + stock_qty = adjust_qty_for_expired_items(item_code, stock_qty, warehouse) if stock_qty[0][0]: in_stock = stock_qty[0][0] > 0 and 1 or 0 @@ -34,20 +34,20 @@ def get_qty_in_stock(item_code, item_warehouse_field, warehouse=None): return frappe._dict({"in_stock": in_stock, "stock_qty": stock_qty, "is_stock_item": is_stock_item}) -def adjust_for_expired_items(item_code, stock_qty, warehouse): - batches = frappe.get_list('Batch', filters=[{'item': item_code}], fields=['expiry_date', 'name']) +def adjust_qty_for_expired_items(item_code, stock_qty, warehouse): + batches = frappe.get_all('Batch', filters=[{'item': item_code}], fields=['expiry_date', 'name']) expired_batches = get_expired_batches(batches) stock_qty = [list(item) for item in stock_qty] - if expired_batches: - for batch in expired_batches: - if warehouse: - stock_qty[0][0] = max(0, stock_qty[0][0] - get_batch_qty(batch, warehouse)) - else: - stock_qty[0][0] = max(0, stock_qty[0][0] - qty_from_all_warehouses(get_batch_qty(batch))) + for batch in expired_batches: + if warehouse: + stock_qty[0][0] = max(0, stock_qty[0][0] - get_batch_qty(batch, warehouse)) + else: + stock_qty[0][0] = max(0, stock_qty[0][0] - qty_from_all_warehouses(get_batch_qty(batch))) + + if not stock_qty[0][0]: + break - if not stock_qty[0][0]: - break return stock_qty From 4990cf7783fbf7b800e36967ac82d43296027ffe Mon Sep 17 00:00:00 2001 From: tundebabzy Date: Wed, 31 Jan 2018 11:35:56 +0100 Subject: [PATCH 07/18] remove stray code --- erpnext/stock/doctype/batch/batch.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index 760643c4ec..3e5d351650 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -47,7 +47,6 @@ def get_batch_qty(batch_no=None, warehouse=None, item_code=None): :param batch_no: Optional - give qty for this batch no :param warehouse: Optional - give qty for this warehouse :param item_code: Optional - give qty for this item''' - frappe.has_permission('Batch', throw=True) out = 0 if batch_no and warehouse: out = float(frappe.db.sql("""select sum(actual_qty) From 82fa04ce323d1094cf32b8ec7f3074f3edd3cfd5 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 31 Jan 2018 22:20:14 +0530 Subject: [PATCH 08/18] Update projects.py --- erpnext/config/projects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/config/projects.py b/erpnext/config/projects.py index b97e097f08..ac11c7e45c 100644 --- a/erpnext/config/projects.py +++ b/erpnext/config/projects.py @@ -15,7 +15,7 @@ def get_data(): { "type": "doctype", "name": "Task", - "route": "Tree/Task", + "route": "List/Task", "description": _("Project activity / task."), }, { From b9ce104b092af55ab3a3b95aa82a3b62054922e4 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 1 Feb 2018 14:58:50 +0530 Subject: [PATCH 09/18] Opening balance in stock ledger report (#12729) --- erpnext/stock/report/stock_ledger/stock_ledger.py | 5 ++--- erpnext/stock/stock_ledger.py | 10 +++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index 8377f59979..e436132ec8 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -106,11 +106,10 @@ def get_opening_balance(filters, columns): from erpnext.stock.stock_ledger import get_previous_sle last_entry = get_previous_sle({ "item_code": filters.item_code, - "warehouse": get_warehouse_condition(filters.warehouse), + "warehouse_condition": get_warehouse_condition(filters.warehouse), "posting_date": filters.from_date, "posting_time": "00:00:00" }) - row = [""]*len(columns) row[1] = _("'Opening'") for i, v in ((9, 'qty_after_transaction'), (11, 'valuation_rate'), (12, 'stock_value')): @@ -122,7 +121,7 @@ def get_warehouse_condition(warehouse): warehouse_details = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"], as_dict=1) if warehouse_details: return " exists (select name from `tabWarehouse` wh \ - where wh.lft >= %s and wh.rgt <= %s and sle.warehouse = wh.name)"%(warehouse_details.lft, + where wh.lft >= %s and wh.rgt <= %s and warehouse = wh.name)"%(warehouse_details.lft, warehouse_details.rgt) return '' diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 647c9faf02..874a382192 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -407,7 +407,12 @@ def get_previous_sle(args, for_update=False): def get_stock_ledger_entries(previous_sle, operator=None, order="desc", limit=None, for_update=False, debug=False): """get stock ledger entries filtered by specific posting datetime conditions""" - conditions = "timestamp(posting_date, posting_time) {0} timestamp(%(posting_date)s, %(posting_time)s)".format(operator) + conditions = " and timestamp(posting_date, posting_time) {0} timestamp(%(posting_date)s, %(posting_time)s)".format(operator) + if previous_sle.get("warehouse"): + conditions += " and warehouse = %(warehouse)s" + elif previous_sle.get("warehouse_condition"): + conditions += " and " + previous_sle.get("warehouse_condition") + if not previous_sle.get("posting_date"): previous_sle["posting_date"] = "1900-01-01" if not previous_sle.get("posting_time"): @@ -418,9 +423,8 @@ def get_stock_ledger_entries(previous_sle, operator=None, order="desc", limit=No return frappe.db.sql("""select *, timestamp(posting_date, posting_time) as "timestamp" from `tabStock Ledger Entry` where item_code = %%(item_code)s - and warehouse = %%(warehouse)s and ifnull(is_cancelled, 'No')='No' - and %(conditions)s + %(conditions)s order by timestamp(posting_date, posting_time) %(order)s, name %(order)s %(limit)s %(for_update)s""" % { "conditions": conditions, From d85247cd7af6d63a553ad143acb801ac225fb204 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 1 Feb 2018 17:18:38 +0530 Subject: [PATCH 10/18] [Fix] Pay button not working in pos --- erpnext/selling/page/point_of_sale/point_of_sale.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js index f5bc6c2499..357475d479 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -1268,7 +1268,7 @@ class NumberPad { this.disable_highlight = disable_highlight; this.reset_btns = reset_btns; this.del_btn = del_btn; - this.disable_btns = disable_btns; + this.disable_btns = disable_btns || []; this.make_dom(); this.bind_events(); this.value = ''; From d4359faa3153cf9b0bd8f3e02c73407699976b4b Mon Sep 17 00:00:00 2001 From: Jay Parikh Date: Thu, 1 Feb 2018 04:26:24 -0800 Subject: [PATCH 11/18] Enable/Disable Discount in POS using POS Profile #11748 --- .../doctype/pos_profile/pos_profile.json | 32 ++++++++++++++- .../doctype/sales_invoice/sales_invoice.py | 3 +- .../page/point_of_sale/point_of_sale.js | 40 ++++++++++++++----- 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json index 2cb8e1024a..ed3de07140 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.json +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json @@ -254,6 +254,36 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "allow_user_to_edit_discount", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Allow user to edit Discount", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -1476,7 +1506,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-01-03 17:30:45.198147", + "modified": "2018-01-31 19:33:11.765731", "modified_by": "Administrator", "module": "Accounts", "name": "POS Profile", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index f37fe6114c..2af30e1956 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -249,7 +249,8 @@ class SalesInvoice(SellingController): if pos: return { "print_format": pos.get("print_format_for_online"), - "allow_edit_rate": pos.get("allow_user_to_edit_rate") + "allow_edit_rate": pos.get("allow_user_to_edit_rate"), + "allow_edit_discount": pos.get("allow_user_to_edit_discount") } def update_time_sheet(self, sales_invoice): diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js index f5bc6c2499..bfbb6ad405 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -464,6 +464,7 @@ erpnext.pos.PointOfSale = class PointOfSale { if (r.message) { this.frm.meta.default_print_format = r.message.print_format || 'POS Invoice'; this.frm.allow_edit_rate = r.message.allow_edit_rate; + this.frm.allow_edit_discount = r.message.allow_edit_discount; } } @@ -553,9 +554,9 @@ class POSCart {
${this.get_taxes_and_totals()}
-
- ${this.get_discount_amount()} -
+
`+ + (!this.frm.allow_edit_discount ? `` : `${this.get_discount_amount()}`)+ + `
${this.get_grand_total()}
@@ -714,6 +715,21 @@ class POSCart { this.customer_field.set_value(this.frm.doc.customer); } + disable_numpad_control() { + if(!this.frm.allow_edit_rate && !this.frm.allow_edit_discount) { + return ['Rate', 'Disc']; + } + if(!this.frm.allow_edit_rate || !this.frm.allow_edit_discount) { + if(!this.frm.allow_edit_rate) { + return ['Rate']; + } else { + return ['Disc']; + } + } else { + return []; + } + } + make_numpad() { this.numpad = new NumberPad({ button_array: [ @@ -728,7 +744,7 @@ class POSCart { disable_highlight: ['Qty', 'Disc', 'Rate', 'Pay'], reset_btns: ['Qty', 'Disc', 'Rate', 'Pay'], del_btn: 'Del', - disable_btns: !this.frm.allow_edit_rate ? ['Rate']: [], + disable_btns: this.disable_numpad_control(), wrapper: this.wrapper.find('.number-pad-container'), onclick: (btn_value) => { // on click @@ -1300,13 +1316,15 @@ class NumberPad { this.set_class(); - this.disable_btns.forEach((btn) => { - const $btn = this.get_btn(btn); - $btn.prop("disabled", true) - $btn.hover(() => { - $btn.css('cursor','not-allowed'); - }) - }) + if(this.disable_btns) { + this.disable_btns.forEach((btn) => { + const $btn = this.get_btn(btn); + $btn.prop("disabled", true) + $btn.hover(() => { + $btn.css('cursor','not-allowed'); + }) + }) + } } set_class() { From 2aba97bff1aec527068053bd03798e2356df4070 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 1 Feb 2018 18:52:33 +0530 Subject: [PATCH 12/18] Fetch items from BOM in Material Request --- erpnext/manufacturing/doctype/bom/bom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index fb52d5260c..2e69475c9a 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -544,7 +544,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite group by item_code, stock_uom order by idx""" - if fetch_exploded: + if cint(fetch_exploded): query = query.format(table="BOM Explosion Item", where_conditions="", select_columns = ", bom_item.source_warehouse, (Select idx from `tabBOM Item` where item_code = bom_item.item_code and parent = %(parent)s ) as idx") From cae13bf048042190dffe76f079827a523ab8e168 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 1 Feb 2018 18:58:14 +0530 Subject: [PATCH 13/18] Fixes in purchase register report (#12737) --- .../item_wise_sales_register/item_wise_sales_register.py | 4 ++-- .../accounts/report/purchase_register/purchase_register.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index ab5251f292..8917c9ff63 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -168,11 +168,11 @@ def get_tax_accounts(item_list, columns, company_currency, for d in item_list: invoice_item_row.setdefault(d.parent, []).append(d) - item_row_map.setdefault(d.parent, {}).setdefault(d.item_code, []).append(d) + item_row_map.setdefault(d.parent, {}).setdefault(d.item_code or d.item_name, []).append(d) conditions = "" if doctype == "Purchase Invoice": - conditions = " and category in ('Total', 'Valuation and Total')" + conditions = " and category in ('Total', 'Valuation and Total') and base_tax_amount_after_discount_amount != 0" tax_details = frappe.db.sql(""" select diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py index 37848d5064..610475acfc 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.py +++ b/erpnext/accounts/report/purchase_register/purchase_register.py @@ -172,6 +172,7 @@ def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts): else sum(base_tax_amount_after_discount_amount) * -1 end as tax_amount from `tabPurchase Taxes and Charges` where parent in (%s) and category in ('Total', 'Valuation and Total') + and base_tax_amount_after_discount_amount != 0 group by parent, account_head, add_deduct_tax """ % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1) From 91c5e4c429b09a03044873f6cd7f55f59804b9e0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 1 Feb 2018 18:58:22 +0530 Subject: [PATCH 14/18] Validate rate in PO with supplier quotation only if checking is enabled via Buying Settings (#12734) --- erpnext/buying/doctype/purchase_order/purchase_order.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index db13bd5520..e879f40c37 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -68,12 +68,16 @@ class PurchaseOrder(BuyingController): }, "Supplier Quotation Item": { "ref_dn_field": "supplier_quotation_item", - "compare_fields": [["rate", "="], ["project", "="], ["item_code", "="], + "compare_fields": [["project", "="], ["item_code", "="], ["uom", "="], ["conversion_factor", "="]], "is_child_table": True } }) + + if cint(frappe.db.get_single_value('Buying Settings', 'maintain_same_rate')): + self.validate_rate_with_reference_doc([["Supplier Quotation", "supplier_quotation", "supplier_quotation_item"]]) + def validate_supplier(self): prevent_po = frappe.db.get_value("Supplier", self.supplier, 'prevent_pos') if prevent_po: From 5df64d84a1b18cc8730430a60423d3e8dc312cb6 Mon Sep 17 00:00:00 2001 From: tundebabzy Date: Thu, 1 Feb 2018 17:05:54 +0100 Subject: [PATCH 15/18] code review fix --- erpnext/utilities/product.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py index 5901b6031d..415056d80b 100644 --- a/erpnext/utilities/product.py +++ b/erpnext/utilities/product.py @@ -22,10 +22,10 @@ def get_qty_in_stock(item_code, item_warehouse_field, warehouse=None): stock_qty = frappe.db.sql("""select GREATEST(actual_qty - reserved_qty, 0) from tabBin where item_code=%s and warehouse=%s""", (item_code, warehouse)) - if stock_qty[0][0]: + if stock_qty: stock_qty = adjust_qty_for_expired_items(item_code, stock_qty, warehouse) - if stock_qty[0][0]: + if stock_qty: in_stock = stock_qty[0][0] > 0 and 1 or 0 if stock_qty: From 171e5af995a8b2de1db5603d8f56b1b8cb13bf1f Mon Sep 17 00:00:00 2001 From: Jay Parikh Date: Thu, 1 Feb 2018 21:39:20 -0800 Subject: [PATCH 16/18] Fixed indentation issue in pull # 11748 --- .../page/point_of_sale/point_of_sale.js | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js index bfbb6ad405..70e45f9512 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -464,7 +464,7 @@ erpnext.pos.PointOfSale = class PointOfSale { if (r.message) { this.frm.meta.default_print_format = r.message.print_format || 'POS Invoice'; this.frm.allow_edit_rate = r.message.allow_edit_rate; - this.frm.allow_edit_discount = r.message.allow_edit_discount; + this.frm.allow_edit_discount = r.message.allow_edit_discount; } } @@ -715,7 +715,7 @@ class POSCart { this.customer_field.set_value(this.frm.doc.customer); } - disable_numpad_control() { + disable_numpad_control() { if(!this.frm.allow_edit_rate && !this.frm.allow_edit_discount) { return ['Rate', 'Disc']; } @@ -1316,15 +1316,15 @@ class NumberPad { this.set_class(); - if(this.disable_btns) { - this.disable_btns.forEach((btn) => { - const $btn = this.get_btn(btn); - $btn.prop("disabled", true) - $btn.hover(() => { - $btn.css('cursor','not-allowed'); - }) - }) - } + if(this.disable_btns) { + this.disable_btns.forEach((btn) => { + const $btn = this.get_btn(btn); + $btn.prop("disabled", true) + $btn.hover(() => { + $btn.css('cursor','not-allowed'); + }) + }) + } } set_class() { From 2c95ab3897571bdfa63e62d0c7ac73dd91db50c6 Mon Sep 17 00:00:00 2001 From: Jay Parikh Date: Thu, 1 Feb 2018 22:52:58 -0800 Subject: [PATCH 17/18] Code fix for Enable/Disable Discount in POS using POS Profile #11748 --- .../page/point_of_sale/point_of_sale.js | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js index 70e45f9512..bc7ebf3abe 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -464,7 +464,7 @@ erpnext.pos.PointOfSale = class PointOfSale { if (r.message) { this.frm.meta.default_print_format = r.message.print_format || 'POS Invoice'; this.frm.allow_edit_rate = r.message.allow_edit_rate; - this.frm.allow_edit_discount = r.message.allow_edit_discount; + this.frm.allow_edit_discount = r.message.allow_edit_discount; } } @@ -585,6 +585,7 @@ class POSCart { this.numpad && this.numpad.reset_value(); this.customer_field.set_value(""); + this.$discount_amount.find('input:text').val(''); this.wrapper.find('.grand-total-value').text( format_currency(this.frm.doc.grand_total, this.frm.currency)); this.wrapper.find('.rounded-total-value').text( @@ -715,19 +716,15 @@ class POSCart { this.customer_field.set_value(this.frm.doc.customer); } - disable_numpad_control() { - if(!this.frm.allow_edit_rate && !this.frm.allow_edit_discount) { - return ['Rate', 'Disc']; + disable_numpad_control() { + let disabled_btns = []; + if(!this.frm.allow_edit_rate) { + disabled_btns.push('Rate'); } - if(!this.frm.allow_edit_rate || !this.frm.allow_edit_discount) { - if(!this.frm.allow_edit_rate) { - return ['Rate']; - } else { - return ['Disc']; - } - } else { - return []; + if(!this.frm.allow_edit_discount) { + disabled_btns.push('Disc'); } + return disabled_btns; } make_numpad() { From 9d2f04139c1a321a501b3b8ca7c928d2213116a6 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Sat, 3 Feb 2018 09:43:12 +0600 Subject: [PATCH 18/18] bumped to version 10.0.19 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 96ad60f866..76123b1a0b 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.0.18' +__version__ = '10.0.19' def get_default_company(user=None): '''Get default company for user'''