Initial commit, basic login working
This commit is contained in:
commit
cbab5ec976
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
WuttaPOS.egg-info/
|
10
CHANGELOG.md
Normal file
10
CHANGELOG.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
# Changelog
|
||||
All notable changes to WuttaPOS will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.1.0] - ??
|
||||
### Added
|
||||
- Initial version (not yet released)
|
4
README.md
Normal file
4
README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
# WuttaPOS
|
||||
|
||||
Why not write a new POS system..? Heh surely a terrible idea, yet here we are.
|
43
setup.cfg
Normal file
43
setup.cfg
Normal file
|
@ -0,0 +1,43 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
|
||||
[metadata]
|
||||
name = WuttaPOS
|
||||
version = attr: wuttapos.__version__
|
||||
author = Lance Edgar
|
||||
author_email = lance@edbob.org
|
||||
url = https://rattailproject.org/
|
||||
license = GNU GPL v3
|
||||
description = Pythonic Point of Sale System
|
||||
long_description = file: README.md
|
||||
classifiers =
|
||||
Development Status :: 3 - Alpha
|
||||
Environment :: Win32 (MS Windows)
|
||||
Environment :: X11 Applications
|
||||
Intended Audience :: Developers
|
||||
License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
||||
Natural Language :: English
|
||||
Operating System :: OS Independent
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 3
|
||||
Topic :: Office/Business :: Financial :: Point-Of-Sale
|
||||
|
||||
|
||||
[options]
|
||||
install_requires =
|
||||
flet
|
||||
psycopg2
|
||||
rattail[db]
|
||||
|
||||
packages = find:
|
||||
include_package_data = True
|
||||
# zip_safe = False
|
||||
|
||||
|
||||
[options.entry_points]
|
||||
|
||||
console_scripts =
|
||||
wuttapos = wuttapos.commands:main
|
||||
|
||||
wuttapos.commands =
|
||||
open = wuttapos.commands:Open
|
||||
status = wuttapos.commands:Status
|
29
setup.py
Normal file
29
setup.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaPOS -- Pythonic Point of Sale System
|
||||
# Copyright © 2023 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 setup script
|
||||
"""
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup()
|
49
tasks.py
Normal file
49
tasks.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaPOS -- Pythonic Point of Sale System
|
||||
# Copyright © 2023 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/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
Tasks for WuttaPOS
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from invoke import task
|
||||
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
exec(open(os.path.join(here, 'wuttapos', '_version.py')).read())
|
||||
|
||||
|
||||
@task
|
||||
def release(c):
|
||||
"""
|
||||
Release a new version of WuttaPOS
|
||||
"""
|
||||
# rebuild local tar.gz file for distribution
|
||||
if os.path.exists('WuttaPOS.egg-info'):
|
||||
shutil.rmtree('WuttaPOS.egg-info')
|
||||
c.run('python -m build --sdist')
|
||||
|
||||
# upload to PyPI
|
||||
filename = f'WuttaPOS-{__version__}.tar.gz'
|
||||
c.run(f'twine upload dist/{filename}')
|
27
wuttapos/__init__.py
Normal file
27
wuttapos/__init__.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaPOS -- Pythonic Point of Sale System
|
||||
# Copyright © 2023 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 - package root
|
||||
"""
|
||||
|
||||
from ._version import __version__
|
3
wuttapos/_version.py
Normal file
3
wuttapos/_version.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
|
||||
__version__ = '0.1.0'
|
115
wuttapos/app.py
Normal file
115
wuttapos/app.py
Normal file
|
@ -0,0 +1,115 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaPOS -- Pythonic Point of Sale System
|
||||
# Copyright © 2023 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 app
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
from rattail.config import make_config
|
||||
|
||||
import flet as ft
|
||||
|
||||
import wuttapos
|
||||
|
||||
|
||||
def main(page: ft.Page):
|
||||
config = make_config()
|
||||
|
||||
page.title = f"WuttaPOS v{wuttapos.__version__}"
|
||||
page.window_full_screen = True
|
||||
# page.vertical_alignment = ft.MainAxisAlignment.CENTER
|
||||
|
||||
# nb. track current user, txn etc.
|
||||
page.shared = {}
|
||||
|
||||
# TODO: this may be too hacky but is useful for now/dev
|
||||
if not config.production():
|
||||
path = os.path.join(config.appdir(), '.wuttapos.cache')
|
||||
if os.path.exists(path):
|
||||
with open(path, 'rt') as f:
|
||||
page.shared = json.loads(f.read())
|
||||
|
||||
def clean_exit():
|
||||
if not config.production():
|
||||
# TODO: this may be too hacky but is useful for now/dev
|
||||
path = os.path.join(config.appdir(), '.wuttapos.cache')
|
||||
with open(path, 'wt') as f:
|
||||
f.write(json.dumps(page.shared))
|
||||
page.window_destroy()
|
||||
|
||||
def keyboard(e):
|
||||
# exit on ctrl+Q
|
||||
if e.ctrl and e.key == 'Q':
|
||||
if not e.shift and not e.alt and not e.meta:
|
||||
clean_exit()
|
||||
|
||||
page.on_keyboard_event = keyboard
|
||||
|
||||
def window_event(e):
|
||||
if e.data == 'close':
|
||||
clean_exit()
|
||||
|
||||
# cf. https://flet.dev/docs/controls/page/#window_destroy
|
||||
page.window_prevent_close = True
|
||||
page.on_window_event = window_event
|
||||
|
||||
# TODO: probably these should be auto-loaded from spec
|
||||
from wuttapos.views.pos import POSView
|
||||
from wuttapos.views.login import LoginView
|
||||
|
||||
# cf .https://flet.dev/docs/guides/python/navigation-and-routing#building-views-on-route-change
|
||||
|
||||
def route_change(route):
|
||||
page.views.clear()
|
||||
if page.route == '/pos':
|
||||
page.views.append(POSView(config, '/pos'))
|
||||
elif page.route == '/login':
|
||||
page.views.append(LoginView(config, '/login'))
|
||||
page.update()
|
||||
|
||||
# TODO: this was in example docs but not sure what it's for?
|
||||
# def view_pop(view):
|
||||
# page.views.pop()
|
||||
# top_view = page.views[-1]
|
||||
# page.go(top_view.route)
|
||||
|
||||
page.on_route_change = route_change
|
||||
# page.on_view_pop = view_pop
|
||||
|
||||
# TODO: this may be too hacky but is useful for now/dev
|
||||
if not config.production() and page.shared.get('user_uuid'):
|
||||
page.go('/pos')
|
||||
else:
|
||||
page.go('/login')
|
||||
|
||||
|
||||
# TODO: can we inject config to the main() via ft.app() kwargs somehow?
|
||||
# pretty sure the `wuttapos open` command is trying to anyway..
|
||||
def run_app(config=None):
|
||||
ft.app(target=main)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_app()
|
74
wuttapos/commands.py
Normal file
74
wuttapos/commands.py
Normal file
|
@ -0,0 +1,74 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaPOS -- Pythonic Point of Sale System
|
||||
# Copyright © 2023 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 commands
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from rattail import commands
|
||||
|
||||
from wuttapos import __version__
|
||||
|
||||
|
||||
def main(*args):
|
||||
"""
|
||||
Main entry point for WuttaPOS command system
|
||||
"""
|
||||
args = list(args or sys.argv[1:])
|
||||
cmd = Command()
|
||||
cmd.run(*args)
|
||||
|
||||
|
||||
class Command(commands.Command):
|
||||
"""
|
||||
Top-level command for WuttaPOS
|
||||
"""
|
||||
name = 'wuttapos'
|
||||
version = __version__
|
||||
description = "WuttaPOS (point of sale)"
|
||||
long_description = ''
|
||||
|
||||
|
||||
class Open(commands.Subcommand):
|
||||
"""
|
||||
Open the Point of Sale app
|
||||
"""
|
||||
name = 'open'
|
||||
description = __doc__.strip()
|
||||
|
||||
def run(self, args):
|
||||
from wuttapos.app import run_app
|
||||
|
||||
run_app(self.config)
|
||||
|
||||
|
||||
class Status(commands.Subcommand):
|
||||
"""
|
||||
Show status of the POS lane
|
||||
"""
|
||||
name = 'status'
|
||||
description = __doc__.strip()
|
||||
|
||||
def run(self, args):
|
||||
print("TODO: show status")
|
25
wuttapos/controls/__init__.py
Normal file
25
wuttapos/controls/__init__.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaPOS -- Pythonic Point of Sale System
|
||||
# Copyright © 2023 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 - flet controls
|
||||
"""
|
35
wuttapos/controls/base.py
Normal file
35
wuttapos/controls/base.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaPOS -- Pythonic Point of Sale System
|
||||
# Copyright © 2023 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 - custom controls (base class)
|
||||
"""
|
||||
|
||||
import flet as ft
|
||||
|
||||
|
||||
class WuttaControl(ft.UserControl):
|
||||
|
||||
def __init__(self, config, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.config = config
|
||||
self.app = config.get_app()
|
88
wuttapos/controls/header.py
Normal file
88
wuttapos/controls/header.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaPOS -- Pythonic Point of Sale System
|
||||
# Copyright © 2023 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 - header control
|
||||
"""
|
||||
|
||||
import flet as ft
|
||||
|
||||
from .base import WuttaControl
|
||||
from .timestamp import WuttaTimestamp
|
||||
|
||||
|
||||
class WuttaHeader(WuttaControl):
|
||||
|
||||
def build(self):
|
||||
self.txn_display = ft.Text("Txn: N", weight=ft.FontWeight.BOLD, size=20)
|
||||
self.user_display = ft.Text("User: N", weight=ft.FontWeight.BOLD, size=20)
|
||||
self.logout_button = ft.FilledButton("Logout", on_click=self.logout_click, visible=False)
|
||||
self.logout_divider = ft.VerticalDivider(visible=False)
|
||||
|
||||
controls = [
|
||||
self.txn_display,
|
||||
ft.VerticalDivider(),
|
||||
ft.Text(f"Cust: N", weight=ft.FontWeight.BOLD, size=20),
|
||||
ft.VerticalDivider(),
|
||||
WuttaTimestamp(self.config, expand=True,
|
||||
weight=ft.FontWeight.BOLD, size=20),
|
||||
self.user_display,
|
||||
ft.VerticalDivider(),
|
||||
self.logout_button,
|
||||
self.logout_divider,
|
||||
ft.Text(f"WuttaPOS", weight=ft.FontWeight.BOLD, size=20),
|
||||
]
|
||||
|
||||
return ft.Row(controls)
|
||||
|
||||
def did_mount(self):
|
||||
self.update_txn_display()
|
||||
self.update_user_display()
|
||||
self.update()
|
||||
|
||||
def update_txn_display(self):
|
||||
txn_display = "N"
|
||||
|
||||
if self.page and self.page.shared and self.page.shared.get('txn_display'):
|
||||
txn_display = self.page.shared['txn_display']
|
||||
|
||||
self.txn_display.value = f"Txn: {txn_display}"
|
||||
|
||||
def update_user_display(self):
|
||||
user_display = "N"
|
||||
|
||||
if self.page and self.page.shared and self.page.shared.get('user_display'):
|
||||
user_display = self.page.shared['user_display']
|
||||
|
||||
self.user_display.value = f"User: {user_display}"
|
||||
|
||||
if self.page and self.page.shared.get('user_uuid'):
|
||||
self.logout_button.visible = True
|
||||
self.logout_divider.visible = True
|
||||
|
||||
def logout_click(self, e):
|
||||
self.page.shared.update({
|
||||
'user_uuid': None,
|
||||
'user_display': None,
|
||||
'txn_display': None,
|
||||
})
|
||||
self.page.go('/login')
|
59
wuttapos/controls/timestamp.py
Normal file
59
wuttapos/controls/timestamp.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaPOS -- Pythonic Point of Sale System
|
||||
# Copyright © 2023 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 - timestamp control
|
||||
"""
|
||||
|
||||
import threading
|
||||
import time
|
||||
|
||||
import flet as ft
|
||||
|
||||
from .base import WuttaControl
|
||||
|
||||
|
||||
class WuttaTimestamp(WuttaControl):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.weight = kwargs.pop('weight', None)
|
||||
self.size = kwargs.pop('size', None)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def build(self):
|
||||
text = self.render_time(self.app.localtime())
|
||||
self.display = ft.Text(text, weight=self.weight, size=self.size)
|
||||
|
||||
thread = threading.Thread(target=self.update_display)
|
||||
thread.start()
|
||||
|
||||
return self.display
|
||||
|
||||
def render_time(self, value):
|
||||
return value.strftime('%a %d %b %Y %I:%M:%S %p')
|
||||
|
||||
def update_display(self):
|
||||
while True:
|
||||
if self.page:
|
||||
self.display.value = self.render_time(self.app.localtime())
|
||||
self.update()
|
||||
time.sleep(0.5)
|
25
wuttapos/views/__init__.py
Normal file
25
wuttapos/views/__init__.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaPOS -- Pythonic Point of Sale System
|
||||
# Copyright © 2023 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 - flet views
|
||||
"""
|
74
wuttapos/views/base.py
Normal file
74
wuttapos/views/base.py
Normal file
|
@ -0,0 +1,74 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaPOS -- Pythonic Point of Sale System
|
||||
# Copyright © 2023 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 - flet views (base class)
|
||||
"""
|
||||
|
||||
import flet as ft
|
||||
|
||||
from wuttapos.controls.header import WuttaHeader
|
||||
|
||||
|
||||
class WuttaView(ft.View):
|
||||
"""
|
||||
Base class for all Flet views used in WuttaPOS
|
||||
"""
|
||||
|
||||
def __init__(self, config, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.config = config
|
||||
self.app = self.config.get_app()
|
||||
self.model = self.app.model
|
||||
|
||||
controls = self.build_controls()
|
||||
self.controls = [
|
||||
WuttaViewContainer(self.config,
|
||||
content=ft.Column(controls=controls),
|
||||
expand=True),
|
||||
]
|
||||
|
||||
def build_controls(self):
|
||||
return [self.build_header()]
|
||||
|
||||
def build_header(self):
|
||||
return WuttaHeader(self.config)
|
||||
|
||||
|
||||
class WuttaViewContainer(ft.Container):
|
||||
"""
|
||||
Main container class to wrap all controls for a view. Used for
|
||||
displaying background image etc.
|
||||
"""
|
||||
|
||||
def __init__(self, config, *args, **kwargs):
|
||||
self.config = config
|
||||
|
||||
if 'image_src' not in kwargs and not self.config.production():
|
||||
# TODO: host a local testing image? where *should* this come from?
|
||||
image = self.config.get('rattail', 'testing_watermark')
|
||||
if image:
|
||||
kwargs['image_src'] = image
|
||||
kwargs.setdefault('image_repeat', ft.ImageRepeat.REPEAT)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
150
wuttapos/views/login.py
Normal file
150
wuttapos/views/login.py
Normal file
|
@ -0,0 +1,150 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaPOS -- Pythonic Point of Sale System
|
||||
# Copyright © 2023 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 - login view
|
||||
"""
|
||||
|
||||
import flet as ft
|
||||
|
||||
from .base import WuttaView
|
||||
|
||||
|
||||
class LoginView(WuttaView):
|
||||
"""
|
||||
Main POS view for WuttaPOS
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
# TODO: maybe support setting this to False? for now that's not 100% supported
|
||||
self.show_username = kwargs.pop('show_username', True)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def build_controls(self):
|
||||
controls = [
|
||||
ft.Row(
|
||||
[ft.Text(value="Welcome to WuttaPOS", weight=ft.FontWeight.BOLD, size=28)],
|
||||
alignment=ft.MainAxisAlignment.CENTER,
|
||||
),
|
||||
ft.Row(),
|
||||
ft.Row(),
|
||||
ft.Row(),
|
||||
]
|
||||
|
||||
if self.show_username:
|
||||
self.username = ft.TextField(label="Login", width=200,
|
||||
on_submit=self.username_submit,
|
||||
autofocus=True)
|
||||
controls.extend([
|
||||
ft.Row(
|
||||
[self.username],
|
||||
alignment=ft.MainAxisAlignment.CENTER,
|
||||
),
|
||||
])
|
||||
|
||||
self.password = ft.TextField(label="Password", width=200, password=True,
|
||||
on_submit=self.password_submit,
|
||||
autofocus=not self.show_username)
|
||||
|
||||
controls.extend([
|
||||
ft.Row(
|
||||
[self.password],
|
||||
alignment=ft.MainAxisAlignment.CENTER,
|
||||
),
|
||||
ft.Row(
|
||||
[
|
||||
ft.FilledButton("Login", on_click=self.attempt_login),
|
||||
ft.ElevatedButton("Clear", on_click=self.clear_login),
|
||||
],
|
||||
alignment=ft.MainAxisAlignment.CENTER,
|
||||
),
|
||||
ft.Row(),
|
||||
ft.Row(
|
||||
[ft.Text("TODO: should have on-screen keyboard (at least 10-key pad?) "
|
||||
"for use with login etc.", italic=True)],
|
||||
alignment=ft.MainAxisAlignment.CENTER,
|
||||
),
|
||||
])
|
||||
|
||||
return [
|
||||
self.build_header(),
|
||||
ft.Column(controls=controls,
|
||||
expand=True,
|
||||
alignment=ft.MainAxisAlignment.CENTER),
|
||||
]
|
||||
|
||||
def username_submit(self, e):
|
||||
if self.username.value:
|
||||
self.password.focus()
|
||||
else:
|
||||
self.username.focus()
|
||||
|
||||
def password_submit(self, e):
|
||||
if self.password.value:
|
||||
self.attempt_login()
|
||||
else:
|
||||
self.password.focus()
|
||||
|
||||
def clear_login(self, e):
|
||||
if self.show_username:
|
||||
self.username.value = ""
|
||||
self.password.value = ""
|
||||
if self.show_username:
|
||||
self.username.focus()
|
||||
else:
|
||||
self.password.focus()
|
||||
self.page.update()
|
||||
|
||||
def attempt_login(self, e=None):
|
||||
if self.show_username and not self.username.value:
|
||||
self.username.focus()
|
||||
return
|
||||
if not self.password.value:
|
||||
self.password.focus()
|
||||
return
|
||||
|
||||
session = self.app.make_session()
|
||||
auth = self.app.get_auth_handler()
|
||||
user = auth.authenticate_user(session, self.username.value, self.password.value)
|
||||
user_display = str(user) if user else None
|
||||
session.close()
|
||||
|
||||
if user:
|
||||
# handle success
|
||||
self.page.shared.update({
|
||||
'user_uuid': user.uuid,
|
||||
'user_display': user_display,
|
||||
})
|
||||
self.page.go('/pos')
|
||||
|
||||
else:
|
||||
# handle failure
|
||||
self.page.snack_bar = ft.SnackBar(ft.Text("Login failed!",
|
||||
color='black',
|
||||
weight=ft.FontWeight.BOLD),
|
||||
bgcolor='yellow',
|
||||
duration=1500)
|
||||
self.page.snack_bar.open = True
|
||||
self.password.focus()
|
||||
self.page.update()
|
102
wuttapos/views/pos.py
Normal file
102
wuttapos/views/pos.py
Normal file
|
@ -0,0 +1,102 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaPOS -- Pythonic Point of Sale System
|
||||
# Copyright © 2023 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 - POS view
|
||||
"""
|
||||
|
||||
import flet as ft
|
||||
|
||||
from .base import WuttaView
|
||||
|
||||
|
||||
class POSView(WuttaView):
|
||||
"""
|
||||
Main POS view for WuttaPOS
|
||||
"""
|
||||
|
||||
def build_controls(self):
|
||||
|
||||
self.main_input = ft.TextField(on_submit=self.main_submit,
|
||||
autofocus=True)
|
||||
|
||||
def make_text(*args, **kwargs):
|
||||
kwargs['weight'] = ft.FontWeight.BOLD
|
||||
kwargs['size'] = 20
|
||||
return ft.Text(*args, **kwargs)
|
||||
|
||||
return [
|
||||
self.build_header(),
|
||||
|
||||
ft.Row(
|
||||
[self.main_input],
|
||||
alignment=ft.MainAxisAlignment.CENTER,
|
||||
),
|
||||
|
||||
ft.Row(),
|
||||
ft.Row(),
|
||||
ft.Row(),
|
||||
|
||||
ft.Row(
|
||||
[ft.Text("TODO: need lots of things yet here...somewhere..")],
|
||||
alignment=ft.MainAxisAlignment.CENTER,
|
||||
),
|
||||
|
||||
ft.Row(
|
||||
[ft.Text("TODO: for instance, items rang up might go here")],
|
||||
),
|
||||
|
||||
ft.DataTable(
|
||||
columns=[
|
||||
ft.DataColumn(make_text("UPC")),
|
||||
ft.DataColumn(make_text("Description")),
|
||||
ft.DataColumn(make_text("Price"), numeric=True),
|
||||
],
|
||||
rows=[
|
||||
ft.DataRow(
|
||||
cells=[
|
||||
ft.DataCell(make_text("0007430500132-1")),
|
||||
ft.DataCell(make_text("Apple Cider Vinegar 32oz")),
|
||||
ft.DataCell(make_text("$5.99")),
|
||||
],
|
||||
),
|
||||
ft.DataRow(
|
||||
cells=[
|
||||
ft.DataCell(make_text("0007430500116-1")),
|
||||
ft.DataCell(make_text("Apple Cider Vinegar 16oz")),
|
||||
ft.DataCell(make_text("$3.59")),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
def main_submit(self, e):
|
||||
value = self.main_input.value.upper()
|
||||
self.page.snack_bar = ft.SnackBar(ft.Text(f"submit: {value}", color='black',
|
||||
weight=ft.FontWeight.BOLD),
|
||||
bgcolor='yellow',
|
||||
duration=1500)
|
||||
self.page.snack_bar.open = True
|
||||
self.main_input.value = ""
|
||||
self.main_input.focus()
|
||||
self.page.update()
|
Loading…
Reference in a new issue