diff --git a/README.md b/README.md index c14950c2..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 --- @@ -48,7 +49,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 +66,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 +77,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) --- 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): diff --git a/posawesome/fixtures/custom_field.json b/posawesome/fixtures/custom_field.json index 9f421998..f4252780 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, @@ -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", @@ -3656,6 +3709,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_search_batch_no", + "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_search_serial_no", + "is_system_generated": 0, + "is_virtual": 0, + "label": "Search by Batch Number", + "length": 0, + "mandatory_depends_on": null, + "modified": "2023-06-05 23:50:13.126933", + "module": null, + "name": "POS Profile-posa_search_batch_no", + "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, @@ -3683,7 +3789,7 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_search_serial_no", + "insert_after": "posa_search_batch_no", "is_system_generated": 0, "is_virtual": 0, "label": "Tax Inclusive", @@ -3868,6 +3974,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 Invoice", + "fetch_from": "shipping_address_name.posa_delivery_charges", + "fetch_if_empty": 1, + "fieldname": "posa_delivery_charges", + "fieldtype": "Link", + "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": "taxes_and_charges", + "is_system_generated": 0, + "is_virtual": 0, + "label": "Delivery Charges", + "length": 0, + "mandatory_depends_on": null, + "modified": "2022-07-25 11:49:02.312879", + "module": null, + "name": "Sales Invoice-posa_delivery_charges", + "no_copy": 0, + "non_negative": 0, + "options": "Delivery Charges", + "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, @@ -3934,10 +4093,63 @@ "docstatus": 0, "doctype": "Custom Field", "dt": "Sales Invoice", - "fetch_from": "shipping_address_name.posa_delivery_charges", - "fetch_if_empty": 1, - "fieldname": "posa_delivery_charges", - "fieldtype": "Link", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "posa_delivery_charges_rate", + "fieldtype": "Currency", + "hidden": 1, + "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_delivery_charges", + "is_system_generated": 0, + "is_virtual": 0, + "label": "Delivery Charges Rate", + "length": 0, + "mandatory_depends_on": null, + "modified": "2022-07-25 11:49:04.797031", + "module": null, + "name": "Sales Invoice-posa_delivery_charges_rate", + "no_copy": 0, + "non_negative": 0, + "options": "currency", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 1, + "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": 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, @@ -3948,18 +4160,18 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "taxes_and_charges", + "insert_after": "posa_server_cache_duration", "is_system_generated": 0, "is_virtual": 0, - "label": "Delivery Charges", + "label": null, "length": 0, "mandatory_depends_on": null, - "modified": "2022-07-25 11:49:02.312879", + "modified": "2023-06-05 16:59:17.793139", "module": null, - "name": "Sales Invoice-posa_delivery_charges", + "name": "POS Profile-column_break_anyol", "no_copy": 0, "non_negative": 0, - "options": "Delivery Charges", + "options": null, "permlevel": 0, "precision": "", "print_hide": 0, @@ -3983,15 +4195,15 @@ "columns": 0, "default": null, "depends_on": null, - "description": null, + "description": "Use Search Limit for Items", "docstatus": 0, "doctype": "Custom Field", - "dt": "Sales Invoice", + "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_delivery_charges_rate", - "fieldtype": "Currency", - "hidden": 1, + "fieldname": "pose_use_limit_search", + "fieldtype": "Check", + "hidden": 0, "hide_border": 0, "hide_days": 0, "hide_seconds": 0, @@ -4001,24 +4213,77 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_delivery_charges", + "insert_after": "column_break_anyol", "is_system_generated": 0, "is_virtual": 0, - "label": "Delivery Charges Rate", + "label": "Use Limit Search", "length": 0, "mandatory_depends_on": null, - "modified": "2022-07-25 11:49:04.797031", + "modified": "2023-06-05 16:59:18.429778", "module": null, - "name": "Sales Invoice-posa_delivery_charges_rate", + "name": "POS Profile-pose_use_limit_search", "no_copy": 0, "non_negative": 0, - "options": "currency", + "options": null, "permlevel": 0, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, "print_width": null, - "read_only": 1, + "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, diff --git a/posawesome/hooks.py b/posawesome/hooks.py index 977cb8a9..21594ac0 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", @@ -239,6 +239,11 @@ "POS Profile-column_break_dqsba", "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", + "POS Profile-posa_search_batch_no", ), ] ], diff --git a/posawesome/posawesome/api/posapp.py b/posawesome/posawesome/api/posapp.py index eee6e6a2..b78225d8 100644 --- a/posawesome/posawesome/api/posapp.py +++ b/posawesome/posawesome/api/posapp.py @@ -12,9 +12,12 @@ 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_gateway_details, get_dummy_message, get_existing_payment_request_amount, ) @@ -28,6 +31,7 @@ ) from frappe.utils.caching import redis_cache + @frappe.whitelist() def get_opening_dialog_data(): data = {} @@ -53,7 +57,7 @@ def get_opening_dialog_data(): fields=["*"], limit_page_length=0, order_by="parent", - ignore_permissions=True + ignore_permissions=True, ) return data @@ -117,23 +121,58 @@ 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) + 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") + 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 = [] @@ -161,11 +200,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, ) @@ -202,13 +242,39 @@ def _get_items(pos_profile, price_list=None): 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 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") @@ -223,7 +289,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 @@ -236,8 +302,9 @@ 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 [], + "batch_no_data": batch_no_data or [], "attributes": attributes or "", "item_attributes": item_attributes or "", } @@ -246,16 +313,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) @@ -390,7 +457,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 @@ -575,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" @@ -599,7 +669,7 @@ def redeeming_customer_credit( { "doctype": "Journal Entry", "voucher_type": "Journal Entry", - "posting_date": nowdate(), + "posting_date": today, "company": invoice_doc.company, } ) @@ -637,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, @@ -771,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") @@ -792,21 +863,24 @@ 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) 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( @@ -814,7 +888,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, } ) @@ -884,6 +958,7 @@ def create_customer( customer_id, customer_name, company, + pos_profile_doc, tax_id=None, mobile_no=None, email_id=None, @@ -893,10 +968,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 +1001,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 +1394,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 +1431,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,7 +1648,7 @@ 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, } ) @@ -1579,3 +1656,37 @@ def auto_create_items(): 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..5652a62c 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(); }, @@ -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 { @@ -918,20 +925,26 @@ 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.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_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 = ''; + 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); } } @@ -1219,6 +1232,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 +1465,7 @@ export default { } } } - if (!item.btach_price) { + if (!item.batch_price) { if ( !item.is_free_item && !item.posa_is_offer && @@ -1588,13 +1610,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 +1628,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 +1640,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..8bf34bb7 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" > @@ -191,8 +192,12 @@ export default { }), watch: { - filtred_items(data_value) { - this.update_items_details(data_value); + filtred_items(new_value, old_value) { + if (!this.pos_profile.pose_use_limit_search) { + if (new_value.length != old_value.length) { + this.update_items_details(new_value); + } + } }, customer_price_list() { this.get_items(); @@ -216,7 +221,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 +244,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 +337,7 @@ export default { } }, enter_event() { + let match = false; if (!this.filtred_items.length || !this.first_search) { return; } @@ -313,18 +347,62 @@ 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 ( + !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(); + } + }, + 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); @@ -367,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', @@ -437,72 +516,95 @@ 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) { + 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 ( + 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 ( - 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/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; } }); }, 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', 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, )