From cbab5ec976c33813a93ebc2acc4d259ad130040f Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 22 Sep 2023 21:39:25 -0500 Subject: [PATCH] Initial commit, basic login working --- .gitignore | 1 + CHANGELOG.md | 10 +++ README.md | 4 + setup.cfg | 43 ++++++++++ setup.py | 29 +++++++ tasks.py | 49 +++++++++++ wuttapos/__init__.py | 27 ++++++ wuttapos/_version.py | 3 + wuttapos/app.py | 115 +++++++++++++++++++++++++ wuttapos/commands.py | 74 ++++++++++++++++ wuttapos/controls/__init__.py | 25 ++++++ wuttapos/controls/base.py | 35 ++++++++ wuttapos/controls/header.py | 88 +++++++++++++++++++ wuttapos/controls/timestamp.py | 59 +++++++++++++ wuttapos/views/__init__.py | 25 ++++++ wuttapos/views/base.py | 74 ++++++++++++++++ wuttapos/views/login.py | 150 +++++++++++++++++++++++++++++++++ wuttapos/views/pos.py | 102 ++++++++++++++++++++++ 18 files changed, 913 insertions(+) create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 README.md create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 tasks.py create mode 100644 wuttapos/__init__.py create mode 100644 wuttapos/_version.py create mode 100644 wuttapos/app.py create mode 100644 wuttapos/commands.py create mode 100644 wuttapos/controls/__init__.py create mode 100644 wuttapos/controls/base.py create mode 100644 wuttapos/controls/header.py create mode 100644 wuttapos/controls/timestamp.py create mode 100644 wuttapos/views/__init__.py create mode 100644 wuttapos/views/base.py create mode 100644 wuttapos/views/login.py create mode 100644 wuttapos/views/pos.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a7479fe --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +WuttaPOS.egg-info/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..0b95b8c --- /dev/null +++ b/CHANGELOG.md @@ -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) diff --git a/README.md b/README.md new file mode 100644 index 0000000..88dbfca --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ + +# WuttaPOS + +Why not write a new POS system..? Heh surely a terrible idea, yet here we are. diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..c7b9f4d --- /dev/null +++ b/setup.cfg @@ -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 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..9f0788f --- /dev/null +++ b/setup.py @@ -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 . +# +################################################################################ +""" +WuttaPOS setup script +""" + +from setuptools import setup + +setup() diff --git a/tasks.py b/tasks.py new file mode 100644 index 0000000..d868274 --- /dev/null +++ b/tasks.py @@ -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 . +# +################################################################################ +""" +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}') diff --git a/wuttapos/__init__.py b/wuttapos/__init__.py new file mode 100644 index 0000000..309891e --- /dev/null +++ b/wuttapos/__init__.py @@ -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 . +# +################################################################################ +""" +WuttaPOS - package root +""" + +from ._version import __version__ diff --git a/wuttapos/_version.py b/wuttapos/_version.py new file mode 100644 index 0000000..e41b669 --- /dev/null +++ b/wuttapos/_version.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8; -*- + +__version__ = '0.1.0' diff --git a/wuttapos/app.py b/wuttapos/app.py new file mode 100644 index 0000000..cbab023 --- /dev/null +++ b/wuttapos/app.py @@ -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 . +# +################################################################################ +""" +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() diff --git a/wuttapos/commands.py b/wuttapos/commands.py new file mode 100644 index 0000000..67a08c8 --- /dev/null +++ b/wuttapos/commands.py @@ -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 . +# +################################################################################ +""" +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") diff --git a/wuttapos/controls/__init__.py b/wuttapos/controls/__init__.py new file mode 100644 index 0000000..55146a5 --- /dev/null +++ b/wuttapos/controls/__init__.py @@ -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 . +# +################################################################################ +""" +WuttaPOS - flet controls +""" diff --git a/wuttapos/controls/base.py b/wuttapos/controls/base.py new file mode 100644 index 0000000..85d8bc3 --- /dev/null +++ b/wuttapos/controls/base.py @@ -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 . +# +################################################################################ +""" +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() diff --git a/wuttapos/controls/header.py b/wuttapos/controls/header.py new file mode 100644 index 0000000..e1aa287 --- /dev/null +++ b/wuttapos/controls/header.py @@ -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 . +# +################################################################################ +""" +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') diff --git a/wuttapos/controls/timestamp.py b/wuttapos/controls/timestamp.py new file mode 100644 index 0000000..0a1b977 --- /dev/null +++ b/wuttapos/controls/timestamp.py @@ -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 . +# +################################################################################ +""" +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) diff --git a/wuttapos/views/__init__.py b/wuttapos/views/__init__.py new file mode 100644 index 0000000..843beca --- /dev/null +++ b/wuttapos/views/__init__.py @@ -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 . +# +################################################################################ +""" +WuttaPOS - flet views +""" diff --git a/wuttapos/views/base.py b/wuttapos/views/base.py new file mode 100644 index 0000000..641867f --- /dev/null +++ b/wuttapos/views/base.py @@ -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 . +# +################################################################################ +""" +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) diff --git a/wuttapos/views/login.py b/wuttapos/views/login.py new file mode 100644 index 0000000..01b98f8 --- /dev/null +++ b/wuttapos/views/login.py @@ -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 . +# +################################################################################ +""" +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() diff --git a/wuttapos/views/pos.py b/wuttapos/views/pos.py new file mode 100644 index 0000000..fb207eb --- /dev/null +++ b/wuttapos/views/pos.py @@ -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 . +# +################################################################################ +""" +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()