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
This commit is contained in:
parent
8fe4c94005
commit
e2e4df4721
|
@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
################################################################################
|
|
||||||
"""
|
|
||||||
WuttaPOS - buttons
|
|
||||||
"""
|
|
||||||
|
|
||||||
from .base import make_button, WuttaButton, WuttaButtonRow
|
|
69
wuttapos/controls/itemlookup_dept.py
Normal file
69
wuttapos/controls/itemlookup_dept.py
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
"""
|
||||||
|
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
|
|
@ -26,7 +26,7 @@ WuttaPOS - button menus
|
||||||
|
|
||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
from wuttapos.controls.buttons import make_button
|
from wuttapos.controls.buttons import make_button, WuttaButtonRow
|
||||||
|
|
||||||
|
|
||||||
class WuttaMenu(ft.Container):
|
class WuttaMenu(ft.Container):
|
||||||
|
@ -58,3 +58,6 @@ class WuttaMenu(ft.Container):
|
||||||
kwargs.setdefault('width', self.default_button_size * 2)
|
kwargs.setdefault('width', self.default_button_size * 2)
|
||||||
kwargs.setdefault('pos', self.pos)
|
kwargs.setdefault('pos', self.pos)
|
||||||
return make_button(*args, **kwargs)
|
return make_button(*args, **kwargs)
|
||||||
|
|
||||||
|
def make_button_row(self, *args, **kwargs):
|
||||||
|
return WuttaButtonRow(*args, **kwargs)
|
||||||
|
|
|
@ -25,33 +25,9 @@ WuttaPOS - "context" menu
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .base import WuttaMenu
|
from .base import WuttaMenu
|
||||||
from wuttapos.controls.buttons import WuttaButtonRow
|
|
||||||
|
|
||||||
|
|
||||||
class WuttaContextMenu(WuttaMenu):
|
class WuttaContextMenu(WuttaMenu):
|
||||||
|
|
||||||
def build_controls(self):
|
def build_controls(self):
|
||||||
return [
|
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'}}),
|
|
||||||
]),
|
|
||||||
]
|
|
||||||
|
|
|
@ -21,18 +21,17 @@
|
||||||
#
|
#
|
||||||
################################################################################
|
################################################################################
|
||||||
"""
|
"""
|
||||||
WuttaPOS - buttons
|
WuttaPOS - menus master control
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
from wuttapos.controls.menus.tenkey import WuttaTenkeyMenu
|
from wuttapos.controls.menus.tenkey import WuttaTenkeyMenu
|
||||||
from wuttapos.controls.menus.meta import WuttaMetaMenu
|
from wuttapos.controls.menus.meta import WuttaMetaMenu
|
||||||
from wuttapos.controls.menus.context import WuttaContextMenu
|
|
||||||
from wuttapos.controls.menus.suspend import WuttaSuspendMenu
|
from wuttapos.controls.menus.suspend import WuttaSuspendMenu
|
||||||
|
|
||||||
|
|
||||||
class WuttaButtonsMaster(ft.Column):
|
class WuttaMenuMaster(ft.Column):
|
||||||
"""
|
"""
|
||||||
Base class and default implementation for "buttons master"
|
Base class and default implementation for "buttons master"
|
||||||
control. This represents the overall button area in POS view.
|
control. This represents the overall button area in POS view.
|
||||||
|
@ -113,7 +112,15 @@ class WuttaButtonsMaster(ft.Column):
|
||||||
##############################
|
##############################
|
||||||
|
|
||||||
def build_context_menu(self):
|
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
|
# suspend
|
|
@ -25,7 +25,6 @@ WuttaPOS - "meta" menu
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .base import WuttaMenu
|
from .base import WuttaMenu
|
||||||
from wuttapos.controls.buttons import WuttaButtonRow
|
|
||||||
|
|
||||||
|
|
||||||
class WuttaMetaMenu(WuttaMenu):
|
class WuttaMetaMenu(WuttaMenu):
|
||||||
|
@ -33,7 +32,7 @@ class WuttaMetaMenu(WuttaMenu):
|
||||||
def build_controls(self):
|
def build_controls(self):
|
||||||
return [
|
return [
|
||||||
|
|
||||||
WuttaButtonRow([
|
self.make_button_row([
|
||||||
|
|
||||||
self.make_button("CUST",
|
self.make_button("CUST",
|
||||||
bgcolor='blue',
|
bgcolor='blue',
|
||||||
|
@ -44,7 +43,7 @@ class WuttaMetaMenu(WuttaMenu):
|
||||||
pos_cmd='void_dwim'),
|
pos_cmd='void_dwim'),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
WuttaButtonRow([
|
self.make_button_row([
|
||||||
|
|
||||||
self.make_button("ITEM",
|
self.make_button("ITEM",
|
||||||
bgcolor='blue',
|
bgcolor='blue',
|
||||||
|
@ -55,7 +54,7 @@ class WuttaMetaMenu(WuttaMenu):
|
||||||
pos_cmd='manager_dwim'),
|
pos_cmd='manager_dwim'),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
WuttaButtonRow([
|
self.make_button_row([
|
||||||
|
|
||||||
self.make_button("OPEN RING",
|
self.make_button("OPEN RING",
|
||||||
font_size=32,
|
font_size=32,
|
||||||
|
@ -67,7 +66,7 @@ class WuttaMetaMenu(WuttaMenu):
|
||||||
pos_cmd='no_sale_dwim'),
|
pos_cmd='no_sale_dwim'),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
WuttaButtonRow([
|
self.make_button_row([
|
||||||
|
|
||||||
self.make_button("Adjust\nPrice",
|
self.make_button("Adjust\nPrice",
|
||||||
font_size=30,
|
font_size=30,
|
||||||
|
|
|
@ -25,7 +25,6 @@ WuttaPOS - "suspend" menu
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .base import WuttaMenu
|
from .base import WuttaMenu
|
||||||
from wuttapos.controls.buttons import WuttaButtonRow
|
|
||||||
|
|
||||||
|
|
||||||
class WuttaSuspendMenu(WuttaMenu):
|
class WuttaSuspendMenu(WuttaMenu):
|
||||||
|
@ -33,7 +32,7 @@ class WuttaSuspendMenu(WuttaMenu):
|
||||||
def build_controls(self):
|
def build_controls(self):
|
||||||
return [
|
return [
|
||||||
|
|
||||||
WuttaButtonRow([
|
self.make_button_row([
|
||||||
|
|
||||||
self.make_button("SUSPEND",
|
self.make_button("SUSPEND",
|
||||||
bgcolor='purple',
|
bgcolor='purple',
|
||||||
|
@ -43,4 +42,26 @@ class WuttaSuspendMenu(WuttaMenu):
|
||||||
bgcolor='purple',
|
bgcolor='purple',
|
||||||
pos_cmd='resume_txn'),
|
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'}}),
|
||||||
|
]),
|
||||||
]
|
]
|
||||||
|
|
|
@ -34,11 +34,11 @@ from .base import WuttaView
|
||||||
from wuttapos.controls.loginform import WuttaLoginForm
|
from wuttapos.controls.loginform import WuttaLoginForm
|
||||||
from wuttapos.controls.custlookup import WuttaCustomerLookup
|
from wuttapos.controls.custlookup import WuttaCustomerLookup
|
||||||
from wuttapos.controls.itemlookup import WuttaProductLookup
|
from wuttapos.controls.itemlookup import WuttaProductLookup
|
||||||
|
from wuttapos.controls.itemlookup_dept import WuttaProductLookupByDepartment
|
||||||
from wuttapos.controls.deptlookup import WuttaDepartmentLookup
|
from wuttapos.controls.deptlookup import WuttaDepartmentLookup
|
||||||
from wuttapos.controls.txnlookup import WuttaTransactionLookup
|
from wuttapos.controls.txnlookup import WuttaTransactionLookup
|
||||||
from wuttapos.controls.txnitem import WuttaTxnItem
|
from wuttapos.controls.txnitem import WuttaTxnItem
|
||||||
from wuttapos.controls.menus.tenkey import WuttaTenkeyMenu
|
from wuttapos.controls.menus.tenkey import WuttaTenkeyMenu
|
||||||
from wuttapos.controls.buttons.master import WuttaButtonsMaster
|
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -568,6 +568,11 @@ class POSView(WuttaView):
|
||||||
bgcolor='green',
|
bgcolor='green',
|
||||||
on_click=self.set_quantity_click)
|
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 [
|
return [
|
||||||
self.build_header(),
|
self.build_header(),
|
||||||
|
|
||||||
|
@ -603,7 +608,7 @@ class POSView(WuttaView):
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
self.items_column,
|
self.items_column,
|
||||||
WuttaButtonsMaster(self.config, pos=self),
|
self.menu_master,
|
||||||
],
|
],
|
||||||
vertical_alignment=ft.CrossAxisAlignment.START,
|
vertical_alignment=ft.CrossAxisAlignment.START,
|
||||||
),
|
),
|
||||||
|
@ -1091,6 +1096,15 @@ class POSView(WuttaView):
|
||||||
self.authorized_action('pos.override_price', self.adjust_price,
|
self.authorized_action('pos.override_price', self.adjust_price,
|
||||||
message="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):
|
def cmd_customer_dwim(self, entry=None, **kwargs):
|
||||||
|
|
||||||
# prompt user to replace customer if already set
|
# prompt user to replace customer if already set
|
||||||
|
@ -1139,6 +1153,40 @@ class POSView(WuttaView):
|
||||||
else:
|
else:
|
||||||
self.item_lookup()
|
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 cmd_manager_dwim(self, entry=None, **kwargs):
|
||||||
|
|
||||||
def toggle_training(e):
|
def toggle_training(e):
|
||||||
|
|
Loading…
Reference in a new issue