From e2e4df47216884e43ccbd5451d323468f5443d79 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 9 Jul 2024 19:37:42 -0500 Subject: [PATCH] feat: add support for dynamic context menu also add pos cmd, `item_menu_dept` - so a custom button can present list of items for a given department --- .../controls/{buttons/base.py => buttons.py} | 0 wuttapos/controls/buttons/__init__.py | 27 -------- wuttapos/controls/itemlookup_dept.py | 69 +++++++++++++++++++ wuttapos/controls/menus/base.py | 5 +- wuttapos/controls/menus/context.py | 26 +------ .../controls/{buttons => menus}/master.py | 15 ++-- wuttapos/controls/menus/meta.py | 9 ++- wuttapos/controls/menus/suspend.py | 25 ++++++- wuttapos/views/pos.py | 52 +++++++++++++- 9 files changed, 162 insertions(+), 66 deletions(-) rename wuttapos/controls/{buttons/base.py => buttons.py} (100%) delete mode 100644 wuttapos/controls/buttons/__init__.py create mode 100644 wuttapos/controls/itemlookup_dept.py rename wuttapos/controls/{buttons => menus}/master.py (88%) diff --git a/wuttapos/controls/buttons/base.py b/wuttapos/controls/buttons.py similarity index 100% rename from wuttapos/controls/buttons/base.py rename to wuttapos/controls/buttons.py diff --git a/wuttapos/controls/buttons/__init__.py b/wuttapos/controls/buttons/__init__.py deleted file mode 100644 index 822a2cc..0000000 --- a/wuttapos/controls/buttons/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8; -*- -################################################################################ -# -# WuttaPOS -- Pythonic Point of Sale System -# Copyright © 2023-2024 Lance Edgar -# -# This file is part of WuttaPOS. -# -# WuttaPOS is free software: you can redistribute it and/or modify it under the -# terms of the GNU General Public License as published by the Free Software -# Foundation, either version 3 of the License, or (at your option) any later -# version. -# -# WuttaPOS is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# WuttaPOS. If not, see . -# -################################################################################ -""" -WuttaPOS - buttons -""" - -from .base import make_button, WuttaButton, WuttaButtonRow diff --git a/wuttapos/controls/itemlookup_dept.py b/wuttapos/controls/itemlookup_dept.py new file mode 100644 index 0000000..a51c0d0 --- /dev/null +++ b/wuttapos/controls/itemlookup_dept.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# WuttaPOS -- Pythonic Point of Sale System +# Copyright © 2023-2024 Lance Edgar +# +# This file is part of WuttaPOS. +# +# WuttaPOS is free software: you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# WuttaPOS is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# WuttaPOS. If not, see . +# +################################################################################ +""" +WuttaPOS - item lookup for department +""" + +from .itemlookup import WuttaProductLookup + + +class WuttaProductLookupByDepartment(WuttaProductLookup): + + def __init__(self, config, department, *args, **kwargs): + + # nb. this forces first query + kwargs.setdefault('initial_search', True) + + kwargs.setdefault('show_search', False) + + super().__init__(config, *args, **kwargs) + model = self.app.model + + if isinstance(department, model.Department): + self.department_key = department.uuid + else: + self.department_key = department + + # TODO: should somehow combine these 2 approaches, so the user can + # still filter items within a department + + # def get_results(self, session, entry): + # return self.app.get_products_handler().search_products(session, entry) + + def get_results(self, session, entry): + org = self.app.get_org_handler() + prod = self.app.get_products_handler() + model = self.app.model + + department = org.get_department(session, self.department_key) + if not department: + raise ValueError(f"department not found: {self.department_key}") + + products = session.query(model.Product)\ + .filter(model.Product.department == department)\ + .all() + + products = [prod.normalize_product(p) + for p in products] + + return products diff --git a/wuttapos/controls/menus/base.py b/wuttapos/controls/menus/base.py index decb22c..8e5dac2 100644 --- a/wuttapos/controls/menus/base.py +++ b/wuttapos/controls/menus/base.py @@ -26,7 +26,7 @@ WuttaPOS - button menus import flet as ft -from wuttapos.controls.buttons import make_button +from wuttapos.controls.buttons import make_button, WuttaButtonRow class WuttaMenu(ft.Container): @@ -58,3 +58,6 @@ class WuttaMenu(ft.Container): kwargs.setdefault('width', self.default_button_size * 2) kwargs.setdefault('pos', self.pos) return make_button(*args, **kwargs) + + def make_button_row(self, *args, **kwargs): + return WuttaButtonRow(*args, **kwargs) diff --git a/wuttapos/controls/menus/context.py b/wuttapos/controls/menus/context.py index d08d21b..aebe9bf 100644 --- a/wuttapos/controls/menus/context.py +++ b/wuttapos/controls/menus/context.py @@ -25,33 +25,9 @@ WuttaPOS - "context" menu """ from .base import WuttaMenu -from wuttapos.controls.buttons import WuttaButtonRow class WuttaContextMenu(WuttaMenu): def build_controls(self): - return [ - - WuttaButtonRow([ - - self.make_button("Cash", - bgcolor='orange', - pos_cmd='tender', - pos_cmd_kwargs={'tender': {'code': 'CA'}}), - - self.make_button("Check", - bgcolor='orange', - pos_cmd='tender', - pos_cmd_kwargs={'tender': {'code': 'CK'}}), - ]), - - WuttaButtonRow([ - - self.make_button("Food Stamps", - bgcolor='orange', - font_size=34, - pos_cmd='tender', - pos_cmd_kwargs={'tender': {'code': 'FS'}}), - ]), - ] + return [] diff --git a/wuttapos/controls/buttons/master.py b/wuttapos/controls/menus/master.py similarity index 88% rename from wuttapos/controls/buttons/master.py rename to wuttapos/controls/menus/master.py index 9d9ddc7..3dc6976 100644 --- a/wuttapos/controls/buttons/master.py +++ b/wuttapos/controls/menus/master.py @@ -21,18 +21,17 @@ # ################################################################################ """ -WuttaPOS - buttons +WuttaPOS - menus master control """ import flet as ft from wuttapos.controls.menus.tenkey import WuttaTenkeyMenu from wuttapos.controls.menus.meta import WuttaMetaMenu -from wuttapos.controls.menus.context import WuttaContextMenu from wuttapos.controls.menus.suspend import WuttaSuspendMenu -class WuttaButtonsMaster(ft.Column): +class WuttaMenuMaster(ft.Column): """ Base class and default implementation for "buttons master" control. This represents the overall button area in POS view. @@ -113,7 +112,15 @@ class WuttaButtonsMaster(ft.Column): ############################## def build_context_menu(self): - return WuttaContextMenu(self.config, pos=self.pos) + spec = self.config.get('wuttapos.menus.context.spec', + default='wuttapos.controls.menus.context:WuttaContextMenu') + factory = self.app.load_object(spec) + return factory(self.config, pos=self.pos) + + def replace_context_menu(self, menu): + controls = menu.build_controls() + self.context_menu.content.controls = controls + self.update() ############################## # suspend diff --git a/wuttapos/controls/menus/meta.py b/wuttapos/controls/menus/meta.py index 5c8b0e9..bfcff60 100644 --- a/wuttapos/controls/menus/meta.py +++ b/wuttapos/controls/menus/meta.py @@ -25,7 +25,6 @@ WuttaPOS - "meta" menu """ from .base import WuttaMenu -from wuttapos.controls.buttons import WuttaButtonRow class WuttaMetaMenu(WuttaMenu): @@ -33,7 +32,7 @@ class WuttaMetaMenu(WuttaMenu): def build_controls(self): return [ - WuttaButtonRow([ + self.make_button_row([ self.make_button("CUST", bgcolor='blue', @@ -44,7 +43,7 @@ class WuttaMetaMenu(WuttaMenu): pos_cmd='void_dwim'), ]), - WuttaButtonRow([ + self.make_button_row([ self.make_button("ITEM", bgcolor='blue', @@ -55,7 +54,7 @@ class WuttaMetaMenu(WuttaMenu): pos_cmd='manager_dwim'), ]), - WuttaButtonRow([ + self.make_button_row([ self.make_button("OPEN RING", font_size=32, @@ -67,7 +66,7 @@ class WuttaMetaMenu(WuttaMenu): pos_cmd='no_sale_dwim'), ]), - WuttaButtonRow([ + self.make_button_row([ self.make_button("Adjust\nPrice", font_size=30, diff --git a/wuttapos/controls/menus/suspend.py b/wuttapos/controls/menus/suspend.py index 6e56770..e0dbffc 100644 --- a/wuttapos/controls/menus/suspend.py +++ b/wuttapos/controls/menus/suspend.py @@ -25,7 +25,6 @@ WuttaPOS - "suspend" menu """ from .base import WuttaMenu -from wuttapos.controls.buttons import WuttaButtonRow class WuttaSuspendMenu(WuttaMenu): @@ -33,7 +32,7 @@ class WuttaSuspendMenu(WuttaMenu): def build_controls(self): return [ - WuttaButtonRow([ + self.make_button_row([ self.make_button("SUSPEND", bgcolor='purple', @@ -43,4 +42,26 @@ class WuttaSuspendMenu(WuttaMenu): bgcolor='purple', pos_cmd='resume_txn'), ]), + + self.make_button_row([ + + self.make_button("Cash", + bgcolor='orange', + pos_cmd='tender', + pos_cmd_kwargs={'tender': {'code': 'CA'}}), + + self.make_button("Check", + bgcolor='orange', + pos_cmd='tender', + pos_cmd_kwargs={'tender': {'code': 'CK'}}), + ]), + + self.make_button_row([ + + self.make_button("Food Stamps", + bgcolor='orange', + font_size=34, + pos_cmd='tender', + pos_cmd_kwargs={'tender': {'code': 'FS'}}), + ]), ] diff --git a/wuttapos/views/pos.py b/wuttapos/views/pos.py index 13877b0..816699d 100644 --- a/wuttapos/views/pos.py +++ b/wuttapos/views/pos.py @@ -34,11 +34,11 @@ from .base import WuttaView from wuttapos.controls.loginform import WuttaLoginForm from wuttapos.controls.custlookup import WuttaCustomerLookup from wuttapos.controls.itemlookup import WuttaProductLookup +from wuttapos.controls.itemlookup_dept import WuttaProductLookupByDepartment from wuttapos.controls.deptlookup import WuttaDepartmentLookup from wuttapos.controls.txnlookup import WuttaTransactionLookup from wuttapos.controls.txnitem import WuttaTxnItem from wuttapos.controls.menus.tenkey import WuttaTenkeyMenu -from wuttapos.controls.buttons.master import WuttaButtonsMaster log = logging.getLogger(__name__) @@ -568,6 +568,11 @@ class POSView(WuttaView): bgcolor='green', on_click=self.set_quantity_click) + spec = self.config.get('wuttapos.menus.master.spec', + default='wuttapos.controls.menus.master:WuttaMenuMaster') + factory = self.app.load_object(spec) + self.menu_master = factory(self.config, pos=self) + return [ self.build_header(), @@ -603,7 +608,7 @@ class POSView(WuttaView): ft.Row( [ self.items_column, - WuttaButtonsMaster(self.config, pos=self), + self.menu_master, ], vertical_alignment=ft.CrossAxisAlignment.START, ), @@ -1091,6 +1096,15 @@ class POSView(WuttaView): self.authorized_action('pos.override_price', self.adjust_price, message="Adjust Price") + def cmd_context_menu(self, entry=None, **kwargs): + """ + Swap out which context menu is currently shown. + """ + spec = self.config.require(f'wuttapos.menus.{entry}.spec') + factory = self.app.load_object(spec) + menu = factory(self.config, pos=self) + self.menu_master.replace_context_menu(menu) + def cmd_customer_dwim(self, entry=None, **kwargs): # prompt user to replace customer if already set @@ -1139,6 +1153,40 @@ class POSView(WuttaView): else: self.item_lookup() + def cmd_item_menu_dept(self, entry=None, **kwargs): + """ + Show item lookup dialog, restricted to the given department. + """ + key = entry + if not key: + raise ValueError("must specify department key") + + org = self.app.get_org_handler() + session = self.app.make_session() + department = org.get_department(session, key) + session.close() + if not department: + raise ValueError(f"department not found: {key}") + + def select(uuid): + self.attempt_add_product(uuid=uuid) + dlg.open = False + self.reset() + + def cancel(e): + dlg.open = False + self.reset() + + dlg = ft.AlertDialog( + title=ft.Text(f"Item from Department: {department.name}"), + content=WuttaProductLookupByDepartment(self.config, department, + page=self.page, + on_select=select, + on_cancel=cancel), + ) + + self.page.open(dlg) + def cmd_manager_dwim(self, entry=None, **kwargs): def toggle_training(e):