From 47f27a51714f999d32fb0bfa21c13a11b1a964c3 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 1 Apr 2022 15:20:40 +0530 Subject: [PATCH] refactor: move scan api to stock utils; add item_info --- erpnext/public/js/utils/barcode_scanner.js | 4 +- .../page/point_of_sale/point_of_sale.py | 34 +------------ erpnext/stock/tests/test_utils.py | 31 ++++++++++++ erpnext/stock/utils.py | 49 +++++++++++++++++++ 4 files changed, 83 insertions(+), 35 deletions(-) create mode 100644 erpnext/stock/tests/test_utils.py diff --git a/erpnext/public/js/utils/barcode_scanner.js b/erpnext/public/js/utils/barcode_scanner.js index abea5fcb20b..0868a6bd76f 100644 --- a/erpnext/public/js/utils/barcode_scanner.js +++ b/erpnext/public/js/utils/barcode_scanner.js @@ -21,9 +21,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { // batch_no: "LOT12", // present if batch was scanned // serial_no: "987XYZ", // present if serial no was scanned // } - this.scan_api = - opts.scan_api || - "erpnext.selling.page.point_of_sale.point_of_sale.search_for_serial_or_batch_or_barcode_number"; + this.scan_api = opts.scan_api || "erpnext.stock.utils.scan_barcode"; } process_scan() { 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 400be4c7327..99afe813cb9 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -10,6 +10,7 @@ from frappe.utils.nestedset import get_root_of from erpnext.accounts.doctype.pos_invoice.pos_invoice import get_stock_availability from erpnext.accounts.doctype.pos_profile.pos_profile import get_child_nodes, get_item_groups +from erpnext.stock.utils import scan_barcode def search_by_term(search_term, warehouse, price_list): @@ -152,38 +153,7 @@ def get_items(start, page_length, price_list, item_group, pos_profile, search_te @frappe.whitelist() def search_for_serial_or_batch_or_barcode_number(search_value: str) -> Dict[str, Optional[str]]: - - # search barcode no - barcode_data = frappe.db.get_value( - "Item Barcode", - {"barcode": search_value}, - ["barcode", "parent as item_code"], - as_dict=True, - ) - if barcode_data: - return barcode_data - - # search serial no - serial_no_data = frappe.db.get_value( - "Serial No", - search_value, - ["name as serial_no", "item_code", "batch_no"], - as_dict=True, - ) - if serial_no_data: - return serial_no_data - - # search batch no - batch_no_data = frappe.db.get_value( - "Batch", - search_value, - ["name as batch_no", "item as item_code"], - as_dict=True, - ) - if batch_no_data: - return batch_no_data - - return {} + return scan_barcode(search_value) def get_conditions(search_term): diff --git a/erpnext/stock/tests/test_utils.py b/erpnext/stock/tests/test_utils.py new file mode 100644 index 00000000000..9ee0c9f3b5a --- /dev/null +++ b/erpnext/stock/tests/test_utils.py @@ -0,0 +1,31 @@ +import frappe +from frappe.tests.utils import FrappeTestCase + +from erpnext.stock.doctype.item.test_item import make_item +from erpnext.stock.utils import scan_barcode + + +class TestStockUtilities(FrappeTestCase): + def test_barcode_scanning(self): + simple_item = make_item(properties={"barcodes": [{"barcode": "12399"}]}) + self.assertEqual(scan_barcode("12399")["item_code"], simple_item.name) + + batch_item = make_item(properties={"has_batch_no": 1, "create_new_batch": 1}) + batch = frappe.get_doc(doctype="Batch", item=batch_item.name).insert() + + batch_scan = scan_barcode(batch.name) + self.assertEqual(batch_scan["item_code"], batch_item.name) + self.assertEqual(batch_scan["batch_no"], batch.name) + self.assertEqual(batch_scan["has_batch_no"], 1) + self.assertEqual(batch_scan["has_serial_no"], 0) + + serial_item = make_item(properties={"has_serial_no": 1}) + serial = frappe.get_doc( + doctype="Serial No", item_code=serial_item.name, serial_no=frappe.generate_hash() + ).insert() + + serial_scan = scan_barcode(serial.name) + self.assertEqual(serial_scan["item_code"], serial_item.name) + self.assertEqual(serial_scan["serial_no"], serial.name) + self.assertEqual(serial_scan["has_batch_no"], 0) + self.assertEqual(serial_scan["has_serial_no"], 1) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 4f1891fd750..d40218e1439 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -3,6 +3,7 @@ import json +from typing import Dict, Optional import frappe from frappe import _ @@ -548,3 +549,51 @@ def check_pending_reposting(posting_date: str, throw_error: bool = True) -> bool ) return bool(reposting_pending) + + +@frappe.whitelist() +def scan_barcode(search_value: str) -> Dict[str, Optional[str]]: + + # search barcode no + barcode_data = frappe.db.get_value( + "Item Barcode", + {"barcode": search_value}, + ["barcode", "parent as item_code"], + as_dict=True, + ) + if barcode_data: + return _update_item_info(barcode_data) + + # search serial no + serial_no_data = frappe.db.get_value( + "Serial No", + search_value, + ["name as serial_no", "item_code", "batch_no"], + as_dict=True, + ) + if serial_no_data: + return _update_item_info(serial_no_data) + + # search batch no + batch_no_data = frappe.db.get_value( + "Batch", + search_value, + ["name as batch_no", "item as item_code"], + as_dict=True, + ) + if batch_no_data: + return _update_item_info(batch_no_data) + + return {} + + +def _update_item_info(scan_result: Dict[str, Optional[str]]) -> Dict[str, Optional[str]]: + if item_code := scan_result.get("item_code"): + if item_info := frappe.get_cached_value( + "Item", + item_code, + ["has_batch_no", "has_serial_no"], + as_dict=True, + ): + scan_result.update(item_info) + return scan_result