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