From 8e7a755aebdc1e8e11f529c6579822623a105380 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 19 Oct 2020 11:37:22 +0530 Subject: [PATCH] feat: show only available items in point of sale (#23667) * feat: show available items in pos * feat: show selected pos profile on pos screen * fix: codacy * fix: codacy --- .../doctype/pos_profile/pos_profile.json | 11 +- .../pos_profile_user/pos_profile_user.json | 150 ++++-------------- erpnext/accounts/doctype/sales_invoice/pos.py | 11 +- erpnext/accounts/page/pos/pos.js | 11 +- .../page/point_of_sale/point_of_sale.py | 45 ++++-- 5 files changed, 91 insertions(+), 137 deletions(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json index fba1bed9dd1..2f5bef56e10 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.json +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_rename": 1, "autoname": "Prompt", "creation": "2013-05-24 12:15:51", @@ -22,6 +23,7 @@ "allow_user_to_edit_discount", "allow_print_before_pay", "display_items_in_stock", + "hide_unavailable_items", "section_break_15", "applicable_for_users", "section_break_11", @@ -389,11 +391,18 @@ "fieldtype": "Link", "label": "Tax Category", "options": "Tax Category" + }, + { + "default": "0", + "fieldname": "hide_unavailable_items", + "fieldtype": "Check", + "label": "Hide Unavailable Items" } ], "icon": "icon-cog", "idx": 1, - "modified": "2020-01-24 15:52:03.797701", + "links": [], + "modified": "2020-10-16 04:33:57.283873", "modified_by": "Administrator", "module": "Accounts", "name": "POS Profile", diff --git a/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.json b/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.json index 2fb66d227b1..f3a21d6475c 100644 --- a/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.json +++ b/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.json @@ -1,123 +1,39 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-10-27 16:46:06.060930", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2017-10-27 16:46:06.060930", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "default", + "user" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "default", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Default", - "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 - }, + "default": "0", + "fieldname": "default", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Default" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "user", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "User", - "length": 0, - "no_copy": 0, - "options": "User", - "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 + "fieldname": "user", + "fieldtype": "Link", + "in_list_view": 1, + "label": "User", + "options": "User" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-11-23 17:13:16.005475", - "modified_by": "Administrator", - "module": "Accounts", - "name": "POS Profile User", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-10-16 04:33:27.594859", + "modified_by": "Administrator", + "module": "Accounts", + "name": "POS Profile User", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ 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 f371bd425c5..64c7f491bb7 100755 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -175,6 +175,13 @@ def get_items_list(pos_profile, company): if args_list: cond = "and i.item_group in (%s)" % (', '.join(['%s'] * len(args_list))) + bin_join = bin_cond = "" + if pos_profile.get('hide_unavailable_items'): + bin_join = ",`tabBin` b" + bin_cond = "and i.item_code = b.item_code and ifnull(b.actual_qty, 0) > 0 " + if pos_profile.get('warehouse'): + bin_cond += "and b.warehouse = {}".format(frappe.db.escape(pos_profile.get('warehouse'))) + return frappe.db.sql(""" select i.name, i.item_code, i.item_name, i.description, i.item_group, i.has_batch_no, @@ -186,11 +193,13 @@ def get_items_list(pos_profile, company): left join `tabItem Default` id on id.parent = i.name and id.company = %s left join `tabItem Tax` it on it.parent = i.name left join `tabUOM Conversion Detail` c on i.name = c.parent and i.sales_uom = c.uom + {bin_join} where i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1 {cond} + {bin_cond} group by i.item_code - """.format(cond=cond), tuple([company] + args_list), as_dict=1) + """.format(cond=cond, bin_join=bin_join, bin_cond=bin_cond), tuple([company] + args_list), as_dict=1) def get_item_groups(pos_profile): diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 1e82e54cdf8..b9b1d293d47 100755 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -81,7 +81,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ me.page.set_indicator(__("Online"), "green") } } - }) + }); }, onload: function () { @@ -278,6 +278,14 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }) }, + set_pos_profile_title(pos_profile) { + this.page.set_title_sub( + ` + ${pos_profile} + ` + ); + }, + get_data_from_server: function (callback) { var me = this; frappe.call({ @@ -286,6 +294,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ freeze_message: __("Master data syncing, it might take some time"), callback: function (r) { localStorage.setItem('doc', JSON.stringify(r.message.doc)); + me.set_pos_profile_title(r.message.pos_profile.name); me.init_master_data(r) me.set_interval_for_si_sync(); me.check_internet_connection(); 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 8e130ba4246..8bebc8f1fce 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -16,7 +16,9 @@ def get_items(start, page_length, price_list, item_group, search_value="", pos_p display_items_in_stock = 0 if pos_profile: - warehouse, display_items_in_stock = frappe.db.get_value('POS Profile', pos_profile, ['warehouse', 'display_items_in_stock']) + warehouse, display_items_in_stock, hide_unavailable_items = frappe.db.get_value( + 'POS Profile', pos_profile, ['warehouse', 'display_items_in_stock', 'hide_unavailable_items'] + ) if not frappe.db.exists('Item Group', item_group): item_group = get_root_of('Item Group') @@ -37,24 +39,31 @@ def get_items(start, page_length, price_list, item_group, search_value="", pos_p lft, rgt = frappe.db.get_value('Item Group', item_group, ['lft', 'rgt']) # locate function is used to sort by closest match from the beginning of the value + bin_join = bin_cond = "" + if hide_unavailable_items: + bin_join = ",`tabBin` b" + bin_cond = "and i.item_code = b.item_code and ifnull(b.actual_qty, 0) > 0 " + if warehouse: + bin_cond += "and b.warehouse = {}".format(frappe.db.escape(warehouse)) + result = [] items_data = frappe.db.sql(""" SELECT - name AS item_code, - item_name, - stock_uom, - image AS item_image, - idx AS idx, - is_stock_item + i.name AS item_code, + i.item_name, + i.stock_uom, + i.image AS item_image, + i.idx AS idx, + i.is_stock_item FROM - `tabItem` + `tabItem` i {bin_join} WHERE disabled = 0 - AND has_variants = 0 - AND is_sales_item = 1 - AND item_group in (SELECT name FROM `tabItem Group` WHERE lft >= {lft} AND rgt <= {rgt}) - AND {condition} + 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}) + {condition} {bin_cond} ORDER BY idx desc LIMIT @@ -64,7 +73,9 @@ def get_items(start, page_length, price_list, item_group, search_value="", pos_p page_length=page_length, lft=lft, rgt=rgt, - condition=condition + condition=condition, + bin_join=bin_join, + bin_cond=bin_cond ), as_dict=1) if items_data: @@ -154,16 +165,16 @@ def search_serial_or_batch_or_barcode_number(search_value): def get_conditions(item_code, serial_no, batch_no, barcode): if serial_no or batch_no or barcode: - return "name = {0}".format(frappe.db.escape(item_code)) + return "and i.name = {0}".format(frappe.db.escape(item_code)) - return """(name like {item_code} - or item_name like {item_code})""".format(item_code = frappe.db.escape('%' + item_code + '%')) + return ("""and (i.name like {item_code} or i.item_name like {item_code})""" + .format(item_code=frappe.db.escape('%' + item_code + '%'))) def get_item_group_condition(pos_profile): cond = "and 1=1" item_groups = get_item_groups(pos_profile) if item_groups: - cond = "and item_group in (%s)"%(', '.join(['%s']*len(item_groups))) + cond = "and i.item_group in (%s)"%(', '.join(['%s']*len(item_groups))) return cond % tuple(item_groups)