From c7a580e2ac78dd8cb3ce1dc1cb4c33b83feea6ac Mon Sep 17 00:00:00 2001 From: Yousef Date: Mon, 5 Jun 2023 14:50:43 +0200 Subject: [PATCH 1/6] chore: update README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c14950c2..f50963cb 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ One-click installing available if you are hosting on FC from [here](https://frap #### Self Hosting: -1. `bench get-app https://github.com/yrestom/POS-Awesome.git` +1. `bench get-app branch version-14 https://github.com/yrestom/POS-Awesome.git` 2. `bench setup requirements` 3. `bench build --app posawesome` 4. `bench restart` @@ -65,7 +65,7 @@ If you are hosting on FC premium support is available [here](https://frappecloud #### Self Hosting: -If you need premium support it is available [here](https://forms.clickup.com/2191574/f/22w6p-14180/8J92BNHRMKG5ZW2EPF) +If you need premium support please email me [here](mailto:info@totrox.com) #### Community Support: @@ -76,7 +76,7 @@ Available in GitHub [discussions](https://github.com/yrestom/POS-Awesome/discuss ### New Features and Bug report: - Please Create Github Issue from [here](https://github.com/yrestom/POS-Awesome/issues/new/choose) after checking the existing issues -- For paid features, you can email me on youssef[at]totrox.com +- For paid features, you can email me [here](mailto:info@totrox.com) --- From 6a6121db42cabcf7dac413ce967d890b0ac6aee3 Mon Sep 17 00:00:00 2001 From: Yousef Date: Mon, 5 Jun 2023 15:10:08 +0200 Subject: [PATCH 2/6] feat: option to Allow Duplicate Customer Names #318 #311 --- posawesome/fixtures/custom_field.json | 267 +++++++++++------- posawesome/hooks.py | 1 + posawesome/posawesome/api/posapp.py | 35 ++- .../posapp/components/pos/UpdateCustomer.vue | 1 + 4 files changed, 182 insertions(+), 122 deletions(-) diff --git a/posawesome/fixtures/custom_field.json b/posawesome/fixtures/custom_field.json index 9f421998..c047ab2f 100644 --- a/posawesome/fixtures/custom_field.json +++ b/posawesome/fixtures/custom_field.json @@ -2066,6 +2066,59 @@ "unique": 0, "width": null }, + { + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "docstatus": 0, + "doctype": "Custom Field", + "dt": "Sales Order", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "posa_additional_notes_section", + "fieldtype": "Section Break", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "insert_after": "items", + "is_system_generated": 0, + "is_virtual": 0, + "label": "Additional Notes", + "length": 0, + "mandatory_depends_on": null, + "modified": "2021-06-21 16:11:59.366893", + "module": null, + "name": "Sales Order-posa_additional_notes_section", + "no_copy": 0, + "non_negative": 0, + "options": null, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "translatable": 0, + "unique": 0, + "width": null + }, { "allow_in_quick_entry": 0, "allow_on_submit": 0, @@ -2172,6 +2225,59 @@ "unique": 0, "width": null }, + { + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "docstatus": 0, + "doctype": "Custom Field", + "dt": "Sales Order", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "posa_notes", + "fieldtype": "Small Text", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "insert_after": "posa_additional_notes_section", + "is_system_generated": 0, + "is_virtual": 0, + "label": "Additional Notes", + "length": 0, + "mandatory_depends_on": null, + "modified": "2021-06-21 16:11:59.829304", + "module": null, + "name": "Sales Order-posa_notes", + "no_copy": 0, + "non_negative": 0, + "options": null, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "translatable": 1, + "unique": 0, + "width": null + }, { "allow_in_quick_entry": 0, "allow_on_submit": 0, @@ -2596,59 +2702,6 @@ "unique": 0, "width": null }, - { - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": null, - "columns": 0, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Sales Order", - "fetch_from": null, - "fetch_if_empty": 0, - "fieldname": "posa_additional_notes_section", - "fieldtype": "Section Break", - "hidden": 0, - "hide_border": 0, - "hide_days": 0, - "hide_seconds": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "insert_after": "items", - "is_system_generated": 0, - "is_virtual": 0, - "label": "Additional Notes", - "length": 0, - "mandatory_depends_on": null, - "modified": "2021-06-21 16:11:59.366893", - "module": null, - "name": "Sales Order-posa_additional_notes_section", - "no_copy": 0, - "non_negative": 0, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 0, - "read_only_depends_on": null, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "translatable": 0, - "unique": 0, - "width": null - }, { "allow_in_quick_entry": 0, "allow_on_submit": 0, @@ -2808,59 +2861,6 @@ "unique": 0, "width": null }, - { - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "columns": 0, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Sales Order", - "fetch_from": null, - "fetch_if_empty": 0, - "fieldname": "posa_notes", - "fieldtype": "Small Text", - "hidden": 0, - "hide_border": 0, - "hide_days": 0, - "hide_seconds": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "insert_after": "posa_additional_notes_section", - "is_system_generated": 0, - "is_virtual": 0, - "label": "Additional Notes", - "length": 0, - "mandatory_depends_on": null, - "modified": "2021-06-21 16:11:59.829304", - "module": null, - "name": "Sales Order-posa_notes", - "no_copy": 0, - "non_negative": 0, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 0, - "read_only_depends_on": null, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "translatable": 1, - "unique": 0, - "width": null - }, { "allow_in_quick_entry": 1, "allow_on_submit": 0, @@ -3497,6 +3497,59 @@ "unique": 0, "width": null }, + { + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "docstatus": 0, + "doctype": "Custom Field", + "dt": "POS Profile", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "posa_allow_duplicate_customer_names", + "fieldtype": "Check", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "insert_after": "posa_auto_set_delivery_charges", + "is_system_generated": 0, + "is_virtual": 0, + "label": "Allow Duplicate Customer Names", + "length": 0, + "mandatory_depends_on": null, + "modified": "2023-06-05 15:55:21.190823", + "module": null, + "name": "POS Profile-posa_allow_duplicate_customer_names", + "no_copy": 0, + "non_negative": 0, + "options": null, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "translatable": 0, + "unique": 0, + "width": null + }, { "allow_in_quick_entry": 0, "allow_on_submit": 0, @@ -3524,7 +3577,7 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_auto_set_delivery_charges", + "insert_after": "posa_allow_duplicate_customer_names", "is_system_generated": 0, "is_virtual": 0, "label": "POS Awesome Advance Settings", diff --git a/posawesome/hooks.py b/posawesome/hooks.py index 977cb8a9..8349b3de 100644 --- a/posawesome/hooks.py +++ b/posawesome/hooks.py @@ -239,6 +239,7 @@ "POS Profile-column_break_dqsba", "POS Profile-posa_use_server_cache", "POS Profile-posa_server_cache_duration", + "POS Profile-posa_allow_duplicate_customer_names", ), ] ], diff --git a/posawesome/posawesome/api/posapp.py b/posawesome/posawesome/api/posapp.py index eee6e6a2..90ddc77c 100644 --- a/posawesome/posawesome/api/posapp.py +++ b/posawesome/posawesome/api/posapp.py @@ -28,6 +28,7 @@ ) from frappe.utils.caching import redis_cache + @frappe.whitelist() def get_opening_dialog_data(): data = {} @@ -53,7 +54,7 @@ def get_opening_dialog_data(): fields=["*"], limit_page_length=0, order_by="parent", - ignore_permissions=True + ignore_permissions=True, ) return data @@ -390,7 +391,9 @@ def update_invoice(data): invoice_doc.update_stock = 0 if len(invoice_doc.payments) == 0: invoice_doc.payments = ref_doc.payments - invoice_doc.paid_amount = invoice_doc.rounded_total or invoice_doc.grand_total or invoice_doc.total + invoice_doc.paid_amount = ( + invoice_doc.rounded_total or invoice_doc.grand_total or invoice_doc.total + ) for payment in invoice_doc.payments: if payment.default: payment.amount = invoice_doc.paid_amount @@ -884,6 +887,7 @@ def create_customer( customer_id, customer_name, company, + pos_profile_doc, tax_id=None, mobile_no=None, email_id=None, @@ -893,10 +897,12 @@ def create_customer( territory=None, customer_type=None, gender=None, - method='create', + method="create", ): - if method == 'create': - if not frappe.db.exists("Customer", {"customer_name": customer_name}): + pos_profile = json.loads(pos_profile_doc) + if method == "create": + is_exist = frappe.db.exists("Customer", {"customer_name": customer_name}) + if pos_profile.get("posa_allow_duplicate_customer_names") or not is_exist: customer = frappe.get_doc( { "doctype": "Customer", @@ -924,7 +930,7 @@ def create_customer( else: frappe.throw(_("Customer already exists")) - elif method == 'update': + elif method == "update": customer_doc = frappe.get_doc("Customer", customer_id) customer_doc.customer_name = customer_name customer_doc.posa_referral_company = company @@ -1317,12 +1323,12 @@ def get_new_payment_request(doc, mop): def get_payment_gateway_account(args): - return frappe.db.get_value( - "Payment Gateway Account", - args, - ["name", "payment_gateway", "payment_account", "message"], - as_dict=1, - ) + return frappe.db.get_value( + "Payment Gateway Account", + args, + ["name", "payment_gateway", "payment_account", "message"], + as_dict=1, + ) def get_existing_payment_request(doc, pay): @@ -1354,7 +1360,7 @@ def make_payment_request(**args): ref_doc = frappe.get_doc(args.dt, args.dn) gateway_account = get_payment_gateway_account(args.get("payment_gateway_account")) if not gateway_account: - frappe.throw(_("Payment Gateway Account not found")) + frappe.throw(_("Payment Gateway Account not found")) grand_total = get_amount(ref_doc, gateway_account.get("payment_account")) if args.loyalty_points and args.dt == "Sales Order": @@ -1571,11 +1577,10 @@ def auto_create_items(): "is_variant_item": 0, "is_stock_item": 1, "opening_stock": 1000, - "valuation_rate" : 50 + i, + "valuation_rate": 50 + i, "standard_rate": 100 + i, } ) print("Creating Item: {}".format(item_code)) item.insert(ignore_permissions=True) frappe.db.commit() - diff --git a/posawesome/public/js/posapp/components/pos/UpdateCustomer.vue b/posawesome/public/js/posapp/components/pos/UpdateCustomer.vue index 648080cf..c49c1122 100644 --- a/posawesome/public/js/posapp/components/pos/UpdateCustomer.vue +++ b/posawesome/public/js/posapp/components/pos/UpdateCustomer.vue @@ -309,6 +309,7 @@ export default { customer_type: this.customer_type, gender: this.gender, method: this.customer_id ? 'update' : 'create', + pos_profile_doc: this.pos_profile, }; frappe.call({ method: 'posawesome.posawesome.api.posapp.create_customer', From e575a011f6863fde966956e15f45c7405f8985ed Mon Sep 17 00:00:00 2001 From: Yousef Date: Mon, 5 Jun 2023 21:48:18 +0200 Subject: [PATCH 3/6] feat: backend items search option as 'Use Limit Search' #316 #313 --- posawesome/fixtures/custom_field.json | 163 +++++++++++++- posawesome/hooks.py | 5 +- posawesome/posawesome/api/posapp.py | 118 +++++++++-- .../js/posapp/components/pos/Invoice.vue | 47 ++-- .../posapp/components/pos/ItemsSelector.vue | 200 ++++++++++++------ .../templates/pages/__pycache__/__init__.py | 0 posawesome/translations/pt.csv | 2 +- setup.py | 22 +- 8 files changed, 431 insertions(+), 126 deletions(-) create mode 100644 posawesome/templates/pages/__pycache__/__init__.py diff --git a/posawesome/fixtures/custom_field.json b/posawesome/fixtures/custom_field.json index c047ab2f..70b5050a 100644 --- a/posawesome/fixtures/custom_field.json +++ b/posawesome/fixtures/custom_field.json @@ -491,7 +491,7 @@ "dt": "Batch", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_btach_price", + "fieldname": "posa_batch_price", "fieldtype": "Float", "hidden": 0, "hide_border": 0, @@ -511,7 +511,7 @@ "mandatory_depends_on": null, "modified": "2020-10-26 02:31:58.913688", "module": null, - "name": "Batch-posa_btach_price", + "name": "Batch-posa_batch_price", "no_copy": 0, "non_negative": 0, "options": null, @@ -4027,6 +4027,59 @@ "unique": 0, "width": null }, + { + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "docstatus": 0, + "doctype": "Custom Field", + "dt": "POS Profile", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "column_break_anyol", + "fieldtype": "Column Break", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "insert_after": "posa_server_cache_duration", + "is_system_generated": 0, + "is_virtual": 0, + "label": null, + "length": 0, + "mandatory_depends_on": null, + "modified": "2023-06-05 16:59:17.793139", + "module": null, + "name": "POS Profile-column_break_anyol", + "no_copy": 0, + "non_negative": 0, + "options": null, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "translatable": 0, + "unique": 0, + "width": null + }, { "allow_in_quick_entry": 0, "allow_on_submit": 0, @@ -4080,6 +4133,112 @@ "unique": 0, "width": null }, + { + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": "Use Search Limit for Items", + "docstatus": 0, + "doctype": "Custom Field", + "dt": "POS Profile", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "pose_use_limit_search", + "fieldtype": "Check", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "insert_after": "column_break_anyol", + "is_system_generated": 0, + "is_virtual": 0, + "label": "Use Limit Search", + "length": 0, + "mandatory_depends_on": null, + "modified": "2023-06-05 16:59:18.429778", + "module": null, + "name": "POS Profile-pose_use_limit_search", + "no_copy": 0, + "non_negative": 0, + "options": null, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "translatable": 0, + "unique": 0, + "width": null + }, + { + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": "500", + "depends_on": "pose_use_limit_search", + "description": "Search Limit for Items\nFor best performance keep this under 1500", + "docstatus": 0, + "doctype": "Custom Field", + "dt": "POS Profile", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "posa_search_limit", + "fieldtype": "Int", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "insert_after": "pose_use_limit_search", + "is_system_generated": 0, + "is_virtual": 0, + "label": "Search Limit Number", + "length": 0, + "mandatory_depends_on": "pose_use_limit_search", + "modified": "2023-06-05 16:59:18.717131", + "module": null, + "name": "POS Profile-posa_search_limit", + "no_copy": 0, + "non_negative": 1, + "options": null, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "translatable": 0, + "unique": 0, + "width": null + }, { "allow_in_quick_entry": 0, "allow_on_submit": 0, diff --git a/posawesome/hooks.py b/posawesome/hooks.py index 8349b3de..1a440ed1 100644 --- a/posawesome/hooks.py +++ b/posawesome/hooks.py @@ -167,7 +167,7 @@ "POS Profile-posa_allow_partial_payment", "POS Profile-posa_allow_credit_sale", "POS Profile-posa_pos_awesome_advance_settings", - "Batch-posa_btach_price", + "Batch-posa_batch_price", "POS Profile-posa_max_discount_allowed", "POS Profile-posa_allow_return", "POS Profile-posa_col_1", @@ -240,6 +240,9 @@ "POS Profile-posa_use_server_cache", "POS Profile-posa_server_cache_duration", "POS Profile-posa_allow_duplicate_customer_names", + "POS Profile-column_break_anyol", + "POS Profile-pose_use_limit_search", + "POS Profile-posa_search_limit", ), ] ], diff --git a/posawesome/posawesome/api/posapp.py b/posawesome/posawesome/api/posapp.py index 90ddc77c..16e5e448 100644 --- a/posawesome/posawesome/api/posapp.py +++ b/posawesome/posawesome/api/posapp.py @@ -14,7 +14,6 @@ from erpnext.accounts.party import get_party_bank_account from erpnext.stock.doctype.batch.batch import get_batch_no, get_batch_qty, set_batch_nos from erpnext.accounts.doctype.payment_request.payment_request import ( - get_gateway_details, get_dummy_message, get_existing_payment_request_amount, ) @@ -118,23 +117,56 @@ def update_opening_shift_data(data, pos_profile): @frappe.whitelist() -def get_items(pos_profile, price_list=None): +def get_items(pos_profile, price_list=None, item_group="", search_value=""): _pos_profile = json.loads(pos_profile) ttl = _pos_profile.get("posa_server_cache_duration") if ttl: - ttl = int(ttl) * 60 + ttl = int(ttl) * 30 @redis_cache(ttl=ttl or 1800) - def __get_items(pos_profile, price_list=None): - return _get_items(pos_profile, price_list) + def __get_items(pos_profile, price_list, item_group, search_value): + return _get_items(pos_profile, price_list, item_group, search_value) - def _get_items(pos_profile, price_list=None): + def _get_items(pos_profile, price_list, item_group, search_value): pos_profile = json.loads(pos_profile) + data = dict() + posa_display_items_in_stock = pos_profile.get("posa_display_items_in_stock") + search_serial_no = pos_profile.get("posa_search_serial_no") + posa_show_template_items = pos_profile.get("posa_show_template_items") + warehouse = pos_profile.get("warehouse") + use_limit_search = pos_profile.get("pose_use_limit_search") + search_limit = 0 + if not price_list: price_list = pos_profile.get("selling_price_list") + + limit = "" + condition = "" condition += get_item_group_condition(pos_profile.get("name")) - if not pos_profile.get("posa_show_template_items"): + + if use_limit_search: + search_limit = pos_profile.get("posa_search_limit") or 500 + if search_value: + data = search_serial_or_batch_or_barcode_number( + search_value, search_serial_no + ) + + item_code = data.get("item_code") if data.get("item_code") else search_value + serial_no = data.get("serial_no") if data.get("serial_no") else "" + batch_no = data.get("batch_no") if data.get("batch_no") else "" + barcode = data.get("barcode") if data.get("barcode") else "" + + condition += get_seearch_items_conditions( + item_code, serial_no, batch_no, barcode + ) + if item_group: + condition += " AND item_group like '%{item_group}%'".format( + item_group=item_group + ) + limit = " LIMIT {search_limit}".format(search_limit=search_limit) + + if not posa_show_template_items: condition += " AND has_variants = 0" result = [] @@ -162,11 +194,12 @@ def _get_items(pos_profile, price_list=None): disabled = 0 AND is_sales_item = 1 AND is_fixed_asset = 0 - {0} + {condition} ORDER BY - name asc + item_name asc + {limit} """.format( - condition + condition=condition, limit=limit ), as_dict=1, ) @@ -204,12 +237,17 @@ def _get_items(pos_profile, price_list=None): fields=["barcode", "posa_uom"], ) serial_no_data = [] - if pos_profile.get("posa_search_serial_no"): + if search_serial_no: serial_no_data = frappe.get_all( "Serial No", - filters={"item_code": item_code, "status": "Active"}, + filters={ + "item_code": item_code, + "status": "Active", + "warehouse": warehouse, + }, fields=["name as serial_no"], ) + item_stock_qty = 0 if pos_profile.get("posa_display_items_in_stock"): item_stock_qty = get_stock_availability( item_code, pos_profile.get("warehouse") @@ -224,7 +262,7 @@ def _get_items(pos_profile, price_list=None): fields=["attribute", "attribute_value"], filters={"parent": item.item_code, "parentfield": "attributes"}, ) - if pos_profile.get("posa_display_items_in_stock") and ( + if posa_display_items_in_stock and ( not item_stock_qty or item_stock_qty < 0 ): pass @@ -237,7 +275,7 @@ def _get_items(pos_profile, price_list=None): "currency": item_price.get("currency") or pos_profile.get("currency"), "item_barcode": item_barcode or [], - "actual_qty": 0, + "actual_qty": item_stock_qty or 0, "serial_no_data": serial_no_data or [], "attributes": attributes or "", "item_attributes": item_attributes or "", @@ -247,16 +285,16 @@ def _get_items(pos_profile, price_list=None): return result if _pos_profile.get("posa_use_server_cache"): - return __get_items(pos_profile, price_list) + return __get_items(pos_profile, price_list, item_group, search_value) else: - return _get_items(pos_profile, price_list) + return _get_items(pos_profile, price_list, item_group, search_value) def get_item_group_condition(pos_profile): - cond = "and 1=1" + 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 item_group in (%s)" % (", ".join(["%s"] * len(item_groups))) return cond % tuple(item_groups) @@ -795,12 +833,15 @@ def _get_items_details(pos_profile, items_data): serial_no_data = frappe.get_all( "Serial No", - filters={"item_code": item_code, "status": "Active"}, + filters={ + "item_code": item_code, + "status": "Active", + "warehouse": warehouse, + }, fields=["name as serial_no"], ) batch_no_data = [] - from erpnext.stock.doctype.batch.batch import get_batch_qty batch_list = get_batch_qty(warehouse=warehouse, item_code=item_code) @@ -817,7 +858,7 @@ def _get_items_details(pos_profile, items_data): "batch_no": batch.batch_no, "batch_qty": batch.qty, "expiry_date": batch_doc.expiry_date, - "btach_price": batch_doc.posa_btach_price, + "batch_price": batch_doc.posa_batch_price, } ) @@ -1584,3 +1625,38 @@ def auto_create_items(): print("Creating Item: {}".format(item_code)) item.insert(ignore_permissions=True) frappe.db.commit() + + +@frappe.whitelist() +def search_serial_or_batch_or_barcode_number(search_value, search_serial_no): + # 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 + if search_serial_no: + serial_no_data = frappe.db.get_value( + "Serial No", search_value, ["name as serial_no", "item_code"], 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 {} + + +def get_seearch_items_conditions(item_code, serial_no, batch_no, barcode): + if serial_no or batch_no or barcode: + return " and name = {0}".format(frappe.db.escape(item_code)) + return """ and (name like {item_code} or item_name like {item_code})""".format( + item_code=frappe.db.escape("%" + item_code + "%") + ) diff --git a/posawesome/public/js/posapp/components/pos/Invoice.vue b/posawesome/public/js/posapp/components/pos/Invoice.vue index 08eceaaa..176d2175 100644 --- a/posawesome/public/js/posapp/components/pos/Invoice.vue +++ b/posawesome/public/js/posapp/components/pos/Invoice.vue @@ -240,7 +240,7 @@ hide-details v-model.number="item.qty" type="number" - @change="calc_sotck_gty(item, $event)" + @change="calc_stock_qty(item, $event)" :disabled="!!item.posa_is_offer || !!item.posa_is_replace" > @@ -864,7 +864,7 @@ export default { if (item.qty == 0) { this.remove_item(item); } - this.calc_sotck_gty(item, item.qty); + this.calc_stock_qty(item, item.qty); this.$forceUpdate(); }, subtract_one(item) { @@ -872,7 +872,7 @@ export default { if (item.qty == 0) { this.remove_item(item); } - this.calc_sotck_gty(item, item.qty); + this.calc_stock_qty(item, item.qty); this.$forceUpdate(); }, @@ -918,14 +918,14 @@ export default { } if (!cur_item.has_batch_no) { cur_item.qty += item.qty || 1; - this.calc_sotck_gty(cur_item, cur_item.qty); + this.calc_stock_qty(cur_item, cur_item.qty); } else { if ( cur_item.stock_qty < cur_item.actual_batch_qty || !cur_item.batch_no ) { cur_item.qty += item.qty || 1; - this.calc_sotck_gty(cur_item, cur_item.qty); + this.calc_stock_qty(cur_item, cur_item.qty); } else { const new_item = this.get_new_item(cur_item); new_item.batch_no = ''; @@ -1219,6 +1219,15 @@ export default { value = false; } } + if (item.qty == 0) { + evntBus.$emit('show_mesage', { + text: __(`Quantity for item '{0}' cannot be Zero (0)`, [ + item.item_name, + ]), + color: 'error', + }); + value = false; + } if ( item.max_discount > 0 && item.discount_percentage > item.max_discount @@ -1443,7 +1452,7 @@ export default { } } } - if (!item.btach_price) { + if (!item.batch_price) { if ( !item.is_free_item && !item.posa_is_offer && @@ -1588,13 +1597,13 @@ export default { item.discount_amount = 0; item.discount_percentage = 0; } - if (item.btach_price) { - item.price_list_rate = item.btach_price * new_uom.conversion_factor; + if (item.batch_price) { + item.price_list_rate = item.batch_price * new_uom.conversion_factor; } this.update_item_detail(item); }, - calc_sotck_gty(item, value) { + calc_stock_qty(item, value) { item.stock_qty = item.conversion_factor * value; }, @@ -1606,13 +1615,9 @@ export default { }); item.serial_no_selected_count = item.serial_no_selected.length; if (item.serial_no_selected_count != item.stock_qty) { - evntBus.$emit('show_mesage', { - text: __(`Selected Serial No QTY is {0} it should be {1}`, [ - item.serial_no_selected_count, - item.stock_qty, - ]), - color: 'warning', - }); + item.qty = item.serial_no_selected_count; + this.calc_stock_qty(item, item.qty); + this.$forceUpdate(); } }, @@ -1622,12 +1627,12 @@ export default { ); item.actual_batch_qty = batch_no.batch_qty; item.batch_no_expiry_date = batch_no.expiry_date; - if (batch_no.btach_price) { - item.btach_price = batch_no.btach_price; - item.price_list_rate = batch_no.btach_price; - item.rate = batch_no.btach_price; + if (batch_no.batch_price) { + item.batch_price = batch_no.batch_price; + item.price_list_rate = batch_no.batch_price; + item.rate = batch_no.batch_price; } else if (update) { - item.btach_price = null; + item.batch_price = null; this.update_item_detail(item); } }, diff --git a/posawesome/public/js/posapp/components/pos/ItemsSelector.vue b/posawesome/public/js/posapp/components/pos/ItemsSelector.vue index 55962373..64d78294 100644 --- a/posawesome/public/js/posapp/components/pos/ItemsSelector.vue +++ b/posawesome/public/js/posapp/components/pos/ItemsSelector.vue @@ -25,7 +25,7 @@ hide-details v-model="debounce_search" @keydown.esc="esc_event" - @keydown.enter="enter_event" + @keydown.enter="search_onchange" ref="debounce_search" > @@ -134,6 +134,7 @@ outlined hide-details v-model="item_group" + v-on:change="search_onchange" > @@ -192,7 +193,9 @@ export default { watch: { filtred_items(data_value) { - this.update_items_details(data_value); + if (!this.pos_profile.pose_use_limit_search) { + this.update_items_details(data_value); + } }, customer_price_list() { this.get_items(); @@ -216,7 +219,20 @@ export default { } const vm = this; this.loading = true; - if (vm.pos_profile.posa_local_storage && localStorage.items_storage) { + let search = this.get_search(this.first_search); + let gr = ''; + let sr = ''; + if (search) { + sr = search; + } + if (vm.item_group != 'ALL') { + gr = vm.item_group.toLowerCase(); + } + if ( + vm.pos_profile.posa_local_storage && + localStorage.items_storage && + !vm.pos_profile.pose_use_limit_search + ) { vm.items = JSON.parse(localStorage.getItem('items_storage')); evntBus.$emit('set_all_items', vm.items); vm.loading = false; @@ -226,16 +242,31 @@ export default { args: { pos_profile: vm.pos_profile, price_list: vm.customer_price_list, + item_group: gr, + search_value: sr, }, callback: function (r) { if (r.message) { vm.items = r.message; evntBus.$emit('set_all_items', vm.items); vm.loading = false; - console.info('loadItmes'); - if (vm.pos_profile.posa_local_storage) { + console.info('Items Loaded'); + if ( + vm.pos_profile.posa_local_storage && + !vm.pos_profile.pose_use_limit_search + ) { localStorage.setItem('items_storage', ''); - localStorage.setItem('items_storage', JSON.stringify(r.message)); + try { + localStorage.setItem( + 'items_storage', + JSON.stringify(r.message) + ); + } catch (e) { + console.error(e); + } + } + if (vm.pos_profile.pose_use_limit_search) { + vm.enter_event(); } } }, @@ -304,6 +335,7 @@ export default { } }, enter_event() { + let match = false; if (!this.filtred_items.length || !this.first_search) { return; } @@ -313,18 +345,41 @@ export default { new_item.item_barcode.forEach((element) => { if (this.search == element.barcode) { new_item.uom = element.posa_uom; + match = true; } }); + if ( + !new_item.to_set_serial_no && + new_item.has_serial_no && + this.pos_profile.posa_search_serial_no + ) { + new_item.serial_no_data.forEach((element) => { + if (this.search && element.serial_no == this.search) { + new_item.to_set_serial_no = this.first_search; + match = true; + } + }); + } if (this.flags.serial_no) { new_item.to_set_serial_no = this.flags.serial_no; } - this.add_item(new_item); - this.search = null; - this.first_search = null; - this.debounce_search = null; - this.flags.serial_no = null; - this.qty = 1; - this.$refs.debounce_search.focus(); + if (match) { + this.add_item(new_item); + this.search = null; + this.first_search = null; + this.debounce_search = null; + this.flags.serial_no = null; + this.qty = 1; + this.$refs.debounce_search.focus(); + } + }, + search_onchange() { + const vm = this; + if (vm.pos_profile.pose_use_limit_search) { + vm.get_items(); + } else { + vm.enter_event(); + } }, get_item_qty(first_search) { let scal_qty = Math.abs(this.qty); @@ -437,72 +492,79 @@ export default { computed: { filtred_items() { this.search = this.get_search(this.first_search); - let filtred_list = []; - let filtred_group_list = []; - if (this.item_group != 'ALL') { - filtred_group_list = this.items.filter((item) => - item.item_group.toLowerCase().includes(this.item_group.toLowerCase()) - ); - } else { - filtred_group_list = this.items; - } - if (!this.search || this.search.length < 3) { - if ( - this.pos_profile.posa_show_template_items && - this.pos_profile.posa_hide_variants_items - ) { - return (filtred_list = filtred_group_list - .filter((item) => !item.variant_of) - .slice(0, 50)); + if (!this.pos_profile.pose_use_limit_search) { + this.search = this.get_search(this.first_search); + let filtred_list = []; + let filtred_group_list = []; + if (this.item_group != 'ALL') { + filtred_group_list = this.items.filter((item) => + item.item_group + .toLowerCase() + .includes(this.item_group.toLowerCase()) + ); } else { - return (filtred_list = filtred_group_list.slice(0, 50)); + filtred_group_list = this.items; } - } else if (this.search) { - filtred_list = filtred_group_list.filter((item) => { - let found = false; - for (let element of item.item_barcode) { - if (element.barcode == this.search) { - found = true; - break; - } + if (!this.search || this.search.length < 3) { + if ( + this.pos_profile.posa_show_template_items && + this.pos_profile.posa_hide_variants_items + ) { + return (filtred_list = filtred_group_list + .filter((item) => !item.variant_of) + .slice(0, 50)); + } else { + return (filtred_list = filtred_group_list.slice(0, 50)); } - return found; - }); - if (filtred_list.length == 0) { - filtred_list = filtred_group_list.filter((item) => - item.item_code.toLowerCase().includes(this.search.toLowerCase()) - ); + } else if (this.search) { + filtred_list = filtred_group_list.filter((item) => { + let found = false; + for (let element of item.item_barcode) { + if (element.barcode == this.search) { + found = true; + break; + } + } + return found; + }); if (filtred_list.length == 0) { filtred_list = filtred_group_list.filter((item) => - item.item_name.toLowerCase().includes(this.search.toLowerCase()) + item.item_code.toLowerCase().includes(this.search.toLowerCase()) ); - } - if ( - filtred_list.length == 0 && - this.pos_profile.posa_search_serial_no - ) { - filtred_list = filtred_group_list.filter((item) => { - let found = false; - for (let element of item.serial_no_data) { - if (element.serial_no == this.search) { - found = true; - this.flags.serial_no = null; - this.flags.serial_no = this.search; - break; + if (filtred_list.length == 0) { + filtred_list = filtred_group_list.filter((item) => + item.item_name.toLowerCase().includes(this.search.toLowerCase()) + ); + } + if ( + filtred_list.length == 0 && + this.pos_profile.posa_search_serial_no + ) { + filtred_list = filtred_group_list.filter((item) => { + let found = false; + for (let element of item.serial_no_data) { + if (element.serial_no == this.search) { + found = true; + this.flags.serial_no = null; + this.flags.serial_no = this.search; + break; + } } - } - return found; - }); + return found; + }); + } } } - } - if ( - this.pos_profile.posa_show_template_items && - this.pos_profile.posa_hide_variants_items - ) { - return filtred_list.filter((item) => !item.variant_of).slice(0, 50); + if ( + this.pos_profile.posa_show_template_items && + this.pos_profile.posa_hide_variants_items + ) { + return filtred_list.filter((item) => !item.variant_of).slice(0, 50); + } else { + return filtred_list.slice(0, 50); + } } else { - return filtred_list.slice(0, 50); + return this.items.slice(0, 50); } }, debounce_search: { diff --git a/posawesome/templates/pages/__pycache__/__init__.py b/posawesome/templates/pages/__pycache__/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/posawesome/translations/pt.csv b/posawesome/translations/pt.csv index 96d1db2a..4c9df35b 100755 --- a/posawesome/translations/pt.csv +++ b/posawesome/translations/pt.csv @@ -104,7 +104,7 @@ DocType: POS Closing Shift,Payment Reconciliation,Reconciliação de Pagamento DocType: POS Closing Shift,Period End Date,Data de Fim de Periodo DocType: POS Closing Shift,Period Start Date,Data de Inicio de Periodo DocType: POS Closing Shift,Posting Date,Data de Postagem -Custom Field - label: Batch-posa_btach_price,Price,Preço +Custom Field - label: Batch-posa_batch_price,Price,Preço DocType: POS Offer,Price Discount Scheme ,Esquema de Desconto de Preço Custom Field - label: Sales Invoice-posa_is_printed,Printed,Imprimido DocType: POS Offer,Product Discount Scheme,Esquema de Desconto de Produto diff --git a/setup.py b/setup.py index c6e780ac..601dbfbc 100644 --- a/setup.py +++ b/setup.py @@ -1,20 +1,20 @@ # -*- coding: utf-8 -*- from setuptools import setup, find_packages -with open('requirements.txt') as f: - install_requires = f.read().strip().split('\n') +with open("requirements.txt") as f: + install_requires = f.read().strip().split("\n") # get version from __version__ variable in posawesome/__init__.py from posawesome import __version__ as version setup( - name='posawesome', - version=version, - description='POS Awesome', - author='Youssef Restom', - author_email='youssef@totrox.com', - packages=find_packages(), - zip_safe=False, - include_package_data=True, - install_requires=install_requires + name="posawesome", + version=version, + description="POS Awesome", + author="Yousef Restom", + author_email="youssef@totrox.com", + packages=find_packages(), + zip_safe=False, + include_package_data=True, + install_requires=install_requires, ) From cba6d0a7c3e0c0672dd55971cd59cf47ad12cfca Mon Sep 17 00:00:00 2001 From: Yousef Date: Tue, 6 Jun 2023 02:19:50 +0200 Subject: [PATCH 4/6] feat: search by batch number #110 --- posawesome/fixtures/custom_field.json | 145 ++++++++++++------ posawesome/hooks.py | 1 + posawesome/posawesome/api/posapp.py | 40 ++++- .../js/posapp/components/pos/Invoice.vue | 19 ++- .../posapp/components/pos/ItemsSelector.vue | 48 +++++- 5 files changed, 195 insertions(+), 58 deletions(-) diff --git a/posawesome/fixtures/custom_field.json b/posawesome/fixtures/custom_field.json index 70b5050a..f4252780 100644 --- a/posawesome/fixtures/custom_field.json +++ b/posawesome/fixtures/custom_field.json @@ -3716,7 +3716,7 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": "1", + "default": null, "depends_on": null, "description": null, "docstatus": 0, @@ -3724,7 +3724,7 @@ "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_tax_inclusive", + "fieldname": "posa_search_batch_no", "fieldtype": "Check", "hidden": 0, "hide_border": 0, @@ -3739,12 +3739,12 @@ "insert_after": "posa_search_serial_no", "is_system_generated": 0, "is_virtual": 0, - "label": "Tax Inclusive", + "label": "Search by Batch Number", "length": 0, "mandatory_depends_on": null, - "modified": "2021-09-06 16:33:33.398280", + "modified": "2023-06-05 23:50:13.126933", "module": null, - "name": "POS Profile-posa_tax_inclusive", + "name": "POS Profile-posa_search_batch_no", "no_copy": 0, "non_negative": 0, "options": null, @@ -3769,7 +3769,7 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": null, + "default": "1", "depends_on": null, "description": null, "docstatus": 0, @@ -3777,8 +3777,8 @@ "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "column_break_dqsba", - "fieldtype": "Column Break", + "fieldname": "posa_tax_inclusive", + "fieldtype": "Check", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -3789,15 +3789,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_tax_inclusive", + "insert_after": "posa_search_batch_no", "is_system_generated": 0, "is_virtual": 0, - "label": null, + "label": "Tax Inclusive", "length": 0, "mandatory_depends_on": null, - "modified": "2023-04-24 14:44:48.969896", + "modified": "2021-09-06 16:33:33.398280", "module": null, - "name": "POS Profile-column_break_dqsba", + "name": "POS Profile-posa_tax_inclusive", "no_copy": 0, "non_negative": 0, "options": null, @@ -3822,7 +3822,7 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": "1", + "default": null, "depends_on": null, "description": null, "docstatus": 0, @@ -3830,8 +3830,8 @@ "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_local_storage", - "fieldtype": "Check", + "fieldname": "column_break_dqsba", + "fieldtype": "Column Break", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -3842,15 +3842,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "column_break_dqsba", + "insert_after": "posa_tax_inclusive", "is_system_generated": 0, "is_virtual": 0, - "label": "Use Browser Local Storage", + "label": null, "length": 0, "mandatory_depends_on": null, - "modified": "2020-11-13 22:14:13.683091", + "modified": "2023-04-24 14:44:48.969896", "module": null, - "name": "POS Profile-posa_local_storage", + "name": "POS Profile-column_break_dqsba", "no_copy": 0, "non_negative": 0, "options": null, @@ -3875,15 +3875,15 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": "0", + "default": "1", "depends_on": null, - "description": "Use Redis cache on the server to speedup initial loads of POS Awesome ", + "description": null, "docstatus": 0, "doctype": "Custom Field", "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_use_server_cache", + "fieldname": "posa_local_storage", "fieldtype": "Check", "hidden": 0, "hide_border": 0, @@ -3895,15 +3895,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_local_storage", + "insert_after": "column_break_dqsba", "is_system_generated": 0, "is_virtual": 0, - "label": "Use Server Cache", + "label": "Use Browser Local Storage", "length": 0, "mandatory_depends_on": null, - "modified": "2023-04-24 14:44:49.219453", + "modified": "2020-11-13 22:14:13.683091", "module": null, - "name": "POS Profile-posa_use_server_cache", + "name": "POS Profile-posa_local_storage", "no_copy": 0, "non_negative": 0, "options": null, @@ -3928,16 +3928,16 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": "30", - "depends_on": "posa_use_server_cache", - "description": "Cache the values for n minutes", + "default": "0", + "depends_on": null, + "description": "Use Redis cache on the server to speedup initial loads of POS Awesome ", "docstatus": 0, "doctype": "Custom Field", "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_server_cache_duration", - "fieldtype": "Int", + "fieldname": "posa_use_server_cache", + "fieldtype": "Check", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -3948,17 +3948,17 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_use_server_cache", + "insert_after": "posa_local_storage", "is_system_generated": 0, "is_virtual": 0, - "label": "Server Cache Duration", + "label": "Use Server Cache", "length": 0, "mandatory_depends_on": null, - "modified": "2023-04-24 14:44:49.341660", + "modified": "2023-04-24 14:44:49.219453", "module": null, - "name": "POS Profile-posa_server_cache_duration", + "name": "POS Profile-posa_use_server_cache", "no_copy": 0, - "non_negative": 1, + "non_negative": 0, "options": null, "permlevel": 0, "precision": "", @@ -4034,16 +4034,16 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": null, - "depends_on": null, - "description": null, + "default": "30", + "depends_on": "posa_use_server_cache", + "description": "Cache the values for n minutes", "docstatus": 0, "doctype": "Custom Field", "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "column_break_anyol", - "fieldtype": "Column Break", + "fieldname": "posa_server_cache_duration", + "fieldtype": "Int", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -4054,17 +4054,17 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_server_cache_duration", + "insert_after": "posa_use_server_cache", "is_system_generated": 0, "is_virtual": 0, - "label": null, + "label": "Server Cache Duration", "length": 0, "mandatory_depends_on": null, - "modified": "2023-06-05 16:59:17.793139", + "modified": "2023-04-24 14:44:49.341660", "module": null, - "name": "POS Profile-column_break_anyol", + "name": "POS Profile-posa_server_cache_duration", "no_copy": 0, - "non_negative": 0, + "non_negative": 1, "options": null, "permlevel": 0, "precision": "", @@ -4133,6 +4133,59 @@ "unique": 0, "width": null }, + { + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "docstatus": 0, + "doctype": "Custom Field", + "dt": "POS Profile", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "column_break_anyol", + "fieldtype": "Column Break", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "insert_after": "posa_server_cache_duration", + "is_system_generated": 0, + "is_virtual": 0, + "label": null, + "length": 0, + "mandatory_depends_on": null, + "modified": "2023-06-05 16:59:17.793139", + "module": null, + "name": "POS Profile-column_break_anyol", + "no_copy": 0, + "non_negative": 0, + "options": null, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "translatable": 0, + "unique": 0, + "width": null + }, { "allow_in_quick_entry": 0, "allow_on_submit": 0, diff --git a/posawesome/hooks.py b/posawesome/hooks.py index 1a440ed1..21594ac0 100644 --- a/posawesome/hooks.py +++ b/posawesome/hooks.py @@ -243,6 +243,7 @@ "POS Profile-column_break_anyol", "POS Profile-pose_use_limit_search", "POS Profile-posa_search_limit", + "POS Profile-posa_search_batch_no", ), ] ], diff --git a/posawesome/posawesome/api/posapp.py b/posawesome/posawesome/api/posapp.py index 16e5e448..b78225d8 100644 --- a/posawesome/posawesome/api/posapp.py +++ b/posawesome/posawesome/api/posapp.py @@ -12,7 +12,11 @@ from erpnext.accounts.doctype.pos_profile.pos_profile import get_item_groups from frappe.utils.background_jobs import enqueue from erpnext.accounts.party import get_party_bank_account -from erpnext.stock.doctype.batch.batch import get_batch_no, get_batch_qty, set_batch_nos +from erpnext.stock.doctype.batch.batch import ( + get_batch_no, + get_batch_qty, + set_batch_nos, +) from erpnext.accounts.doctype.payment_request.payment_request import ( get_dummy_message, get_existing_payment_request_amount, @@ -129,9 +133,11 @@ def __get_items(pos_profile, price_list, item_group, search_value): def _get_items(pos_profile, price_list, item_group, search_value): pos_profile = json.loads(pos_profile) + today = nowdate() data = dict() posa_display_items_in_stock = pos_profile.get("posa_display_items_in_stock") search_serial_no = pos_profile.get("posa_search_serial_no") + search_batch_no = pos_profile.get("posa_search_batch_no") posa_show_template_items = pos_profile.get("posa_show_template_items") warehouse = pos_profile.get("warehouse") use_limit_search = pos_profile.get("pose_use_limit_search") @@ -236,6 +242,27 @@ def _get_items(pos_profile, price_list, item_group, search_value): filters={"parent": item_code}, fields=["barcode", "posa_uom"], ) + batch_no_data = [] + if search_batch_no: + batch_list = get_batch_qty(warehouse=warehouse, item_code=item_code) + if batch_list: + for batch in batch_list: + if batch.qty > 0 and batch.batch_no: + batch_doc = frappe.get_cached_doc( + "Batch", batch.batch_no + ) + if ( + str(batch_doc.expiry_date) > str(today) + or batch_doc.expiry_date in ["", None] + ) and batch_doc.disabled == 0: + batch_no_data.append( + { + "batch_no": batch.batch_no, + "batch_qty": batch.qty, + "expiry_date": batch_doc.expiry_date, + "batch_price": batch_doc.posa_batch_price, + } + ) serial_no_data = [] if search_serial_no: serial_no_data = frappe.get_all( @@ -277,6 +304,7 @@ def _get_items(pos_profile, price_list, item_group, search_value): "item_barcode": item_barcode or [], "actual_qty": item_stock_qty or 0, "serial_no_data": serial_no_data or [], + "batch_no_data": batch_no_data or [], "attributes": attributes or "", "item_attributes": item_attributes or "", } @@ -616,6 +644,7 @@ def redeeming_customer_credit( invoice_doc, data, is_payment_entry, total_cash, cash_account ): # redeeming customer credit with journal voucher + today = nowdate() if data.get("redeemed_customer_credit"): cost_center = frappe.get_value( "POS Profile", invoice_doc.pos_profile, "cost_center" @@ -640,7 +669,7 @@ def redeeming_customer_credit( { "doctype": "Journal Entry", "voucher_type": "Journal Entry", - "posting_date": nowdate(), + "posting_date": today, "company": invoice_doc.company, } ) @@ -678,7 +707,7 @@ def redeeming_customer_credit( payment_entry_doc = frappe.get_doc( { "doctype": "Payment Entry", - "posting_date": nowdate(), + "posting_date": today, "payment_type": "Receive", "party_type": "Customer", "party": invoice_doc.customer, @@ -812,6 +841,7 @@ def __get_items_details(pos_profile, items_data): return _get_items_details(pos_profile, items_data) def _get_items_details(pos_profile, items_data): + today = nowdate() pos_profile = json.loads(pos_profile) items_data = json.loads(items_data) warehouse = pos_profile.get("warehouse") @@ -848,9 +878,9 @@ def _get_items_details(pos_profile, items_data): if batch_list: for batch in batch_list: if batch.qty > 0 and batch.batch_no: - batch_doc = frappe.get_doc("Batch", batch.batch_no) + batch_doc = frappe.get_cached_doc("Batch", batch.batch_no) if ( - str(batch_doc.expiry_date) > str(nowdate()) + str(batch_doc.expiry_date) > str(today) or batch_doc.expiry_date in ["", None] ) and batch_doc.disabled == 0: batch_no_data.append( diff --git a/posawesome/public/js/posapp/components/pos/Invoice.vue b/posawesome/public/js/posapp/components/pos/Invoice.vue index 176d2175..5652a62c 100644 --- a/posawesome/public/js/posapp/components/pos/Invoice.vue +++ b/posawesome/public/js/posapp/components/pos/Invoice.vue @@ -887,7 +887,8 @@ export default { el.item_code === item.item_code && el.uom === item.uom && !el.posa_is_offer && - !el.posa_is_replace + !el.posa_is_replace && + el.batch_no === item.batch_no ); } if (index === -1 || this.new_line) { @@ -897,6 +898,12 @@ export default { new_item.serial_no_selected.push(item.to_set_serial_no); item.to_set_serial_no = null; } + if (item.has_batch_no && item.to_set_batch_no) { + new_item.batch_no = item.to_set_batch_no; + item.to_set_batch_no = null; + item.batch_no = null; + this.set_batch_qty(new_item, new_item.batch_no, false); + } this.items.unshift(new_item); this.update_item_detail(new_item); } else { @@ -921,17 +928,23 @@ export default { this.calc_stock_qty(cur_item, cur_item.qty); } else { if ( - cur_item.stock_qty < cur_item.actual_batch_qty || + (cur_item.stock_qty < cur_item.actual_batch_qty && + cur_item.batch_no == item.batch_no) || !cur_item.batch_no ) { cur_item.qty += item.qty || 1; this.calc_stock_qty(cur_item, cur_item.qty); } else { const new_item = this.get_new_item(cur_item); - new_item.batch_no = ''; + new_item.batch_no = item.batch_no || item.to_set_batch_no; new_item.batch_no_expiry_date = ''; new_item.actual_batch_qty = ''; new_item.qty = item.qty || 1; + if (new_item.batch_no) { + this.set_batch_qty(new_item, new_item.batch_no, false); + item.to_set_batch_no = null; + item.batch_no = null; + } this.items.unshift(new_item); } } diff --git a/posawesome/public/js/posapp/components/pos/ItemsSelector.vue b/posawesome/public/js/posapp/components/pos/ItemsSelector.vue index 64d78294..8bf34bb7 100644 --- a/posawesome/public/js/posapp/components/pos/ItemsSelector.vue +++ b/posawesome/public/js/posapp/components/pos/ItemsSelector.vue @@ -192,9 +192,11 @@ export default { }), watch: { - filtred_items(data_value) { + filtred_items(new_value, old_value) { if (!this.pos_profile.pose_use_limit_search) { - this.update_items_details(data_value); + if (new_value.length != old_value.length) { + this.update_items_details(new_value); + } } }, customer_price_list() { @@ -363,12 +365,33 @@ export default { if (this.flags.serial_no) { new_item.to_set_serial_no = this.flags.serial_no; } - if (match) { + if ( + !new_item.to_set_batch_no && + new_item.has_batch_no && + this.pos_profile.posa_search_batch_no + ) { + new_item.batch_no_data.forEach((element) => { + if (this.search && element.batch_no == this.search) { + new_item.to_set_batch_no = this.first_search; + new_item.batch_no = this.first_search; + match = true; + } + }); + } + if (this.flags.batch_no) { + new_item.to_set_batch_no = this.flags.batch_no; + } + if ( + match || + (!this.pos_profile.posa_search_serial_no && + !this.pos_profile.search_batch_no) + ) { this.add_item(new_item); this.search = null; this.first_search = null; this.debounce_search = null; this.flags.serial_no = null; + this.flags.batch_no = null; this.qty = 1; this.$refs.debounce_search.focus(); } @@ -422,6 +445,7 @@ export default { this.$refs.debounce_search.focus(); }, update_items_details(items) { + // set debugger const vm = this; frappe.call({ method: 'posawesome.posawesome.api.posapp.get_items_details', @@ -493,7 +517,6 @@ export default { filtred_items() { this.search = this.get_search(this.first_search); if (!this.pos_profile.pose_use_limit_search) { - this.search = this.get_search(this.first_search); let filtred_list = []; let filtred_group_list = []; if (this.item_group != 'ALL') { @@ -553,6 +576,23 @@ export default { return found; }); } + if ( + filtred_list.length == 0 && + this.pos_profile.posa_search_batch_no + ) { + filtred_list = filtred_group_list.filter((item) => { + let found = false; + for (let element of item.batch_no_data) { + if (element.batch_no == this.search) { + found = true; + this.flags.batch_no = null; + this.flags.batch_no = this.search; + break; + } + } + return found; + }); + } } } if ( From db45d278e1fd8259180f04adae393e262eb75060 Mon Sep 17 00:00:00 2001 From: Yousef Date: Tue, 6 Jun 2023 02:20:49 +0200 Subject: [PATCH 5/6] Version: 4.4.0 --- README.md | 1 + posawesome/__init__.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f50963cb..08300c16 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ 26. Supports Customer and Customer Group price list 27. Supports Sales Person 28. Supports Delivery Charges +29. Search and add items by Batch Number --- diff --git a/posawesome/__init__.py b/posawesome/__init__.py index 7f630e56..d8e69ad7 100644 --- a/posawesome/__init__.py +++ b/posawesome/__init__.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import frappe -__version__ = "4.3.2" +__version__ = "4.4.0" def console(*data): From bc12c773079ad147155aa2dbbc59ae1e08553f2c Mon Sep 17 00:00:00 2001 From: Yousef Date: Tue, 6 Jun 2023 02:29:37 +0200 Subject: [PATCH 6/6] feat: disable submit button when click onced so as to prevent opening 2 shifts #309 --- .../public/js/posapp/components/pos/OpeningDialog.vue | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/posawesome/public/js/posapp/components/pos/OpeningDialog.vue b/posawesome/public/js/posapp/components/pos/OpeningDialog.vue index cfba3a08..d8b04490 100644 --- a/posawesome/public/js/posapp/components/pos/OpeningDialog.vue +++ b/posawesome/public/js/posapp/components/pos/OpeningDialog.vue @@ -63,7 +63,13 @@ Cancel - Submit + Submit @@ -76,6 +82,7 @@ export default { props: ['dialog'], data: () => ({ dialog_data: {}, + is_loading: false, companys: [], company: '', pos_profiles_data: [], @@ -155,6 +162,7 @@ export default { if (!this.payments_methods.length || !this.company || !this.pos_profile) { return; } + this.is_loading = true; const vm = this; return frappe .call('posawesome.posawesome.api.posapp.create_opening_voucher', { @@ -167,6 +175,7 @@ export default { evntBus.$emit('register_pos_data', r.message); evntBus.$emit('set_company', r.message.company); vm.close_opening_dialog(); + is_loading = false; } }); },