From b53ce2873c617ccbe9f1e815e37ddbe7b8896f3b Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 10:53:47 +0500 Subject: [PATCH] refactor: restructure utils and events as PR#43 Moves install.py, uninstall.py, validations.py to pos_next/utils/. Splits realtime_events.py into pos_next/events/{stock,invoice,pos_profile}.py. Refactors utils.py into pos_next/utils/version.py and adds package init. Updates hooks.py to reflect new paths. --- pos_next/events/invoice.py | 46 ++++++++++ pos_next/events/pos_profile.py | 57 ++++++++++++ .../{realtime_events.py => events/stock.py} | 89 +------------------ pos_next/hooks.py | 28 +++--- pos_next/utils/__init__.py | 1 + pos_next/{ => utils}/install.py | 0 pos_next/{ => utils}/uninstall.py | 0 pos_next/{ => utils}/validations.py | 0 pos_next/{utils.py => utils/version.py} | 3 +- 9 files changed, 121 insertions(+), 103 deletions(-) create mode 100644 pos_next/events/invoice.py create mode 100644 pos_next/events/pos_profile.py rename pos_next/{realtime_events.py => events/stock.py} (54%) create mode 100644 pos_next/utils/__init__.py rename pos_next/{ => utils}/install.py (100%) rename pos_next/{ => utils}/uninstall.py (100%) rename pos_next/{ => utils}/validations.py (100%) rename pos_next/{utils.py => utils/version.py} (95%) diff --git a/pos_next/events/invoice.py b/pos_next/events/invoice.py new file mode 100644 index 00000000..66d405c2 --- /dev/null +++ b/pos_next/events/invoice.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2024, POS Next and contributors +# For license information, please see license.txt + +""" +Real-time event handlers for invoice creation. +""" + +import frappe +from frappe import _ + +def emit_invoice_created_event(doc, method=None): + """ + Emit real-time event when invoice is created. + + This can be used to notify other terminals about new sales, + update dashboards, or trigger other real-time UI updates. + + Args: + doc: Sales Invoice document + method: Hook method name + """ + if not doc.is_pos: + return + + try: + event_data = { + "invoice_name": doc.name, + "grand_total": doc.grand_total, + "customer": doc.customer, + "pos_profile": doc.pos_profile, + "timestamp": frappe.utils.now(), + } + + frappe.publish_realtime( + event="pos_invoice_created", + message=event_data, + user=None, + after_commit=True + ) + + except Exception as e: + frappe.log_error( + title=_("Real-time Invoice Created Event Error"), + message=f"Failed to emit invoice created event for {doc.name}: {str(e)}" + ) diff --git a/pos_next/events/pos_profile.py b/pos_next/events/pos_profile.py new file mode 100644 index 00000000..3d5dc0b1 --- /dev/null +++ b/pos_next/events/pos_profile.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2024, POS Next and contributors +# For license information, please see license.txt + +""" +Real-time event handlers for POS profile updates. +""" + +import frappe +from frappe import _ + +def emit_pos_profile_updated_event(doc, method=None): + """ + Emit real-time event when POS Profile is updated. + + This event notifies all connected POS terminals about configuration changes, + particularly item group filters, allowing them to clear their cache and reload + items automatically without manual intervention. + + Args: + doc: POS Profile document + method: Hook method name (on_update, validate, etc.) + """ + try: + # Check if item_groups have changed by comparing with the original doc + if doc.has_value_changed("item_groups"): + # Extract current item groups + current_item_groups = [{"item_group": ig.item_group} for ig in doc.get("item_groups", [])] + + # Prepare event data + event_data = { + "pos_profile": doc.name, + "item_groups": current_item_groups, + "timestamp": frappe.utils.now(), + "change_type": "item_groups_updated" + } + + # Emit event to all connected clients + # Event name: pos_profile_updated + # Clients can subscribe to this event and invalidate their cache + frappe.publish_realtime( + event="pos_profile_updated", + message=event_data, + user=None, # Broadcast to all users + after_commit=True # Only emit after successful DB commit + ) + + frappe.logger().info( + f"Emitted pos_profile_updated event for {doc.name} - item groups changed" + ) + + except Exception as e: + # Log error but don't fail the transaction + frappe.log_error( + title=_("Real-time POS Profile Update Event Error"), + message=f"Failed to emit POS profile update event for {doc.name}: {str(e)}" + ) diff --git a/pos_next/realtime_events.py b/pos_next/events/stock.py similarity index 54% rename from pos_next/realtime_events.py rename to pos_next/events/stock.py index 26b8ba66..a9742049 100644 --- a/pos_next/realtime_events.py +++ b/pos_next/events/stock.py @@ -3,8 +3,7 @@ # For license information, please see license.txt """ -Real-time event handlers for POS Next. -Emits Socket.IO events when stock-affecting transactions occur. +Real-time event handlers for stock updates. """ import frappe @@ -12,7 +11,6 @@ from pos_next.api.items import get_stock_quantities - def emit_stock_update_event(doc, method=None): """ Emit real-time stock update event when Sales Invoice is submitted. @@ -98,88 +96,3 @@ def emit_stock_update_event(doc, method=None): title=_("Real-time Stock Update Event Error"), message=f"Failed to emit stock update event for {doc.name}: {str(e)}" ) - - -def emit_invoice_created_event(doc, method=None): - """ - Emit real-time event when invoice is created. - - This can be used to notify other terminals about new sales, - update dashboards, or trigger other real-time UI updates. - - Args: - doc: Sales Invoice document - method: Hook method name - """ - if not doc.is_pos: - return - - try: - event_data = { - "invoice_name": doc.name, - "grand_total": doc.grand_total, - "customer": doc.customer, - "pos_profile": doc.pos_profile, - "timestamp": frappe.utils.now(), - } - - frappe.publish_realtime( - event="pos_invoice_created", - message=event_data, - user=None, - after_commit=True - ) - - except Exception as e: - frappe.log_error( - title=_("Real-time Invoice Created Event Error"), - message=f"Failed to emit invoice created event for {doc.name}: {str(e)}" - ) - - -def emit_pos_profile_updated_event(doc, method=None): - """ - Emit real-time event when POS Profile is updated. - - This event notifies all connected POS terminals about configuration changes, - particularly item group filters, allowing them to clear their cache and reload - items automatically without manual intervention. - - Args: - doc: POS Profile document - method: Hook method name (on_update, validate, etc.) - """ - try: - # Check if item_groups have changed by comparing with the original doc - if doc.has_value_changed("item_groups"): - # Extract current item groups - current_item_groups = [{"item_group": ig.item_group} for ig in doc.get("item_groups", [])] - - # Prepare event data - event_data = { - "pos_profile": doc.name, - "item_groups": current_item_groups, - "timestamp": frappe.utils.now(), - "change_type": "item_groups_updated" - } - - # Emit event to all connected clients - # Event name: pos_profile_updated - # Clients can subscribe to this event and invalidate their cache - frappe.publish_realtime( - event="pos_profile_updated", - message=event_data, - user=None, # Broadcast to all users - after_commit=True # Only emit after successful DB commit - ) - - frappe.logger().info( - f"Emitted pos_profile_updated event for {doc.name} - item groups changed" - ) - - except Exception as e: - # Log error but don't fail the transaction - frappe.log_error( - title=_("Real-time POS Profile Update Event Error"), - message=f"Failed to emit POS profile update event for {doc.name}: {str(e)}" - ) diff --git a/pos_next/hooks.py b/pos_next/hooks.py index ef969bae..6cd223f3 100644 --- a/pos_next/hooks.py +++ b/pos_next/hooks.py @@ -1,4 +1,4 @@ -from pos_next.utils import get_build_version +from pos_next.utils.version import get_build_version app_name = "pos_next" app_title = "POS Next" @@ -121,15 +121,15 @@ # Installation # ------------ -# before_install = "pos_next.install.before_install" -after_install = "pos_next.install.after_install" -after_migrate = "pos_next.install.after_migrate" +# before_install = "pos_next.utils.install.before_install" +after_install = "pos_next.utils.install.after_install" +after_migrate = "pos_next.utils.install.after_migrate" # Uninstallation # ------------ -before_uninstall = "pos_next.uninstall.before_uninstall" -# after_uninstall = "pos_next.uninstall.after_uninstall" +before_uninstall = "pos_next.utils.uninstall.before_uninstall" +# after_uninstall = "pos_next.utils.uninstall.after_uninstall" # Integration Setup # ------------------ @@ -169,7 +169,7 @@ # ---------------- # Custom query for company-aware item filtering standard_queries = { - "Item": "pos_next.validations.item_query" + "Item": "pos_next.utils.validations.item_query" } # DocType Class @@ -186,17 +186,17 @@ doc_events = { "Item": { - "validate": "pos_next.validations.validate_item" + "validate": "pos_next.utils.validations.validate_item" }, "Sales Invoice": { "validate": "pos_next.api.sales_invoice_hooks.validate", "before_cancel": "pos_next.api.sales_invoice_hooks.before_cancel", - "on_submit": "pos_next.realtime_events.emit_stock_update_event", - "on_cancel": "pos_next.realtime_events.emit_stock_update_event", - "after_insert": "pos_next.realtime_events.emit_invoice_created_event" + "on_submit": "pos_next.events.stock.emit_stock_update_event", + "on_cancel": "pos_next.events.stock.emit_stock_update_event", + "after_insert": "pos_next.events.invoice.emit_invoice_created_event" }, "POS Profile": { - "on_update": "pos_next.realtime_events.emit_pos_profile_updated_event" + "on_update": "pos_next.events.pos_profile.emit_pos_profile_updated_event" } } @@ -219,7 +219,7 @@ # Testing # ------- -# before_tests = "pos_next.install.before_tests" +# before_tests = "pos_next.utils.install.before_tests" # Overriding Methods # ------------------------------ @@ -293,4 +293,4 @@ # } -website_route_rules = [{'from_route': '/pos/', 'to_route': 'pos'},] \ No newline at end of file +website_route_rules = [{'from_route': '/pos/', 'to_route': 'pos'},] diff --git a/pos_next/utils/__init__.py b/pos_next/utils/__init__.py new file mode 100644 index 00000000..9e77dcf7 --- /dev/null +++ b/pos_next/utils/__init__.py @@ -0,0 +1 @@ +from .version import get_build_version, get_app_version diff --git a/pos_next/install.py b/pos_next/utils/install.py similarity index 100% rename from pos_next/install.py rename to pos_next/utils/install.py diff --git a/pos_next/uninstall.py b/pos_next/utils/uninstall.py similarity index 100% rename from pos_next/uninstall.py rename to pos_next/utils/uninstall.py diff --git a/pos_next/validations.py b/pos_next/utils/validations.py similarity index 100% rename from pos_next/validations.py rename to pos_next/utils/validations.py diff --git a/pos_next/utils.py b/pos_next/utils/version.py similarity index 95% rename from pos_next/utils.py rename to pos_next/utils/version.py index fe5655b9..7ae07a34 100644 --- a/pos_next/utils.py +++ b/pos_next/utils/version.py @@ -8,7 +8,8 @@ from pos_next import __version__ as app_version -_BASE_DIR = Path(__file__).resolve().parent +# _BASE_DIR should point to the pos_next app directory +_BASE_DIR = Path(__file__).resolve().parent.parent _VERSION_FILE = _BASE_DIR / "public" / "pos" / "version.json" _MANIFEST_FILE = _BASE_DIR / "public" / "pos" / "manifest.webmanifest" _FALLBACK_VERSION: str | None = None