Compare commits
7 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c97b8c080e | |||
| bf6494f2a9 | |||
| 4f86c4bcaa | |||
| 960dbfdee6 | |||
| 7225d6b453 | |||
| d6c1e688b1 | |||
| 64f1e8897a |
10 changed files with 279 additions and 30 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1 +1,4 @@
|
|||
*~
|
||||
*.pyc
|
||||
dist/
|
||||
docs/_build/
|
||||
|
|
|
|||
12
CHANGELOG.md
12
CHANGELOG.md
|
|
@ -5,6 +5,18 @@ 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).
|
||||
|
||||
## v0.4.2 (2026-01-04)
|
||||
|
||||
### Fix
|
||||
|
||||
- tweak installer slightly, per upstream changes
|
||||
|
||||
## v0.4.1 (2026-01-03)
|
||||
|
||||
### Fix
|
||||
|
||||
- add basic support for install/setup of terminal
|
||||
|
||||
## v0.4.0 (2026-01-03)
|
||||
|
||||
First release under the new project.
|
||||
|
|
|
|||
10
README.md
10
README.md
|
|
@ -1,17 +1,19 @@
|
|||
|
||||
# WuttaPOS
|
||||
|
||||
This is an old idea but a new effort, for a Python-based point of sale
|
||||
system.
|
||||
|
||||
This project includes two primary components:
|
||||
|
||||
- web app and related daemons, to run on the server
|
||||
- standalone GUI app, to run on the lanes
|
||||
|
||||
This project is in the very early stages and is not yet documented.
|
||||
It is based on an earlier effort, which used Rattail:
|
||||
[rattail/wuttapos](https://forgejo.wuttaproject.org/rattail/wuttapos)
|
||||
But in any case there *are* some docs, at
|
||||
https://docs.wuttaproject.org/wuttapos/
|
||||
|
||||
However this project uses Wutta Framework, has no Rattail
|
||||
dependencies, and "starts over" for (mostly) everything.
|
||||
The docs below will go away at some point, but are left here for now.
|
||||
|
||||
|
||||
## Server
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ For now this only describes a *development* setup, which assumes the following:
|
|||
* Python 3.11 or newer
|
||||
* database in PostgreSQL or MySQL
|
||||
|
||||
These steps will setup both the server and terminal in a shared
|
||||
These steps will setup both the Server and Terminal in a shared
|
||||
virtual environment.
|
||||
|
||||
|
||||
|
|
@ -40,13 +40,13 @@ Install Server
|
|||
|
||||
**Please note, you must create your database before running the installer.**
|
||||
|
||||
Run the WuttaPOS installer to setup the server app:
|
||||
Run the WuttaPOS installer to setup the Server app:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
bin/wuttapos install
|
||||
|
||||
Now you can run the server app via command line:
|
||||
Now you can run the Server app via command line:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
|
|
@ -86,4 +86,48 @@ should be improved - but that will come later.
|
|||
Install Terminal
|
||||
----------------
|
||||
|
||||
TODO: this is not yet documented
|
||||
**Please note, this assumes you already installed the Server as described above.**
|
||||
|
||||
For now, we'll just install the Terminal alongside the Server. They
|
||||
will share the same virtual environment, installed code, app database,
|
||||
and "most of" the config files.
|
||||
|
||||
So within your virtual environment, run the same installer again:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
bin/wuttapos install
|
||||
|
||||
.. note::
|
||||
|
||||
You will be asked for store and terminal IDs - these should be
|
||||
valid and refer to actual records for those types, within the
|
||||
database. See the Admin menu of the Server web app to manage those
|
||||
records.
|
||||
|
||||
At this point the Terminal app should be ready to go. To run the
|
||||
standalone GUI:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
bin/wuttapos -c app/terminal.conf run
|
||||
|
||||
You will need to login; the POS uses a special set of permissions but
|
||||
the first admin user should already have all that are needed. So just
|
||||
login using same credentials as for the Server app.
|
||||
|
||||
It's also possible (due to the magic of Flet/Flutter) to *serve* the
|
||||
Terminal as a web app:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
bin/wuttapos -c app/terminal.conf serve
|
||||
|
||||
In that case (by default) you can browse it at http://localhost:8332
|
||||
|
||||
That might be useful for online demo purposes etc. but definitely the
|
||||
intention is to run as standalone GUI for production. (However the
|
||||
story around tablets is TBD.)
|
||||
|
||||
For now, running as a web service like that is not a supported
|
||||
feature.
|
||||
|
|
|
|||
|
|
@ -8,15 +8,11 @@ build-backend = "hatchling.build"
|
|||
name = "WuttaPOS"
|
||||
# nb. 0.3.0 was the last release of the previous rattail WuttaPOS app.
|
||||
# pretty sure this will bump to 0.4.0 and then i can remove this note.
|
||||
version = "0.4.0"
|
||||
version = "0.4.2"
|
||||
description = "Point of Sale system based on Wutta Framework"
|
||||
readme = "README.md"
|
||||
authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
|
||||
classifiers = [
|
||||
# TODO: remove this if you intend to publish your project
|
||||
# (it's here by default, to prevent accidental publishing)
|
||||
"Private :: Do Not Upload",
|
||||
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Environment :: Win32 (MS Windows)",
|
||||
"Environment :: X11 Applications",
|
||||
|
|
@ -36,7 +32,7 @@ classifiers = [
|
|||
]
|
||||
dependencies = [
|
||||
"psycopg2",
|
||||
"WuttJamaican[db]>=0.28.2",
|
||||
"WuttJamaican[db]>=0.28.6",
|
||||
"WuttaSync",
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@
|
|||
See also: :ref:`wuttapos-run`
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import typer
|
||||
|
||||
from wuttapos.cli import wuttapos_typer
|
||||
|
|
@ -37,4 +39,11 @@ def run(ctx: typer.Context):
|
|||
from wuttapos.terminal.app import run_app
|
||||
|
||||
config = ctx.parent.wutta_config
|
||||
run_app(config)
|
||||
|
||||
# nb. it does not seem possible (?) to inject our config when
|
||||
# launching the Flet app, which means it will create a *separate*
|
||||
# config for itself..this should ensure it has the right settings.
|
||||
# see also notes in wuttapos.terminal.app module, for run_app()
|
||||
os.environ["WUTTA_CONFIG_FILES"] = os.pathsep.join(config.get_prioritized_files())
|
||||
|
||||
run_app()
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
Install handler for WuttaPOS
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
|
@ -37,14 +38,62 @@ class InstallHandler(base.InstallHandler):
|
|||
|
||||
template_paths = ["wuttapos:installer-templates"]
|
||||
|
||||
store_id = None
|
||||
terminal_id = None
|
||||
alongside = False
|
||||
|
||||
def do_install_steps(self):
|
||||
|
||||
# prompt for install type first
|
||||
self.get_install_type()
|
||||
|
||||
# install server/terminal dependencies
|
||||
self.install_app_deps()
|
||||
|
||||
# then everything else
|
||||
super().do_install_steps()
|
||||
|
||||
def sanity_check(self):
|
||||
"""
|
||||
We override this because the normal up-front sanity check
|
||||
includes a check for the app dir. But we need to delay that
|
||||
one a bit, so behavior depends on which "install type" we're
|
||||
doing.
|
||||
"""
|
||||
|
||||
def check_appdir(self):
|
||||
"""
|
||||
We bypass the normal check here, if the current install is for
|
||||
terminal alongside server.
|
||||
"""
|
||||
if self.install_type == "terminal":
|
||||
|
||||
# does appdir exist yet?
|
||||
appdir = os.path.join(sys.prefix, "app")
|
||||
if os.path.exists(appdir):
|
||||
|
||||
# install alongside server?
|
||||
self.alongside = self.prompt_bool(
|
||||
"install alongside server?", default=False
|
||||
)
|
||||
if self.alongside:
|
||||
# this mode expects the appdir to exist, so continue
|
||||
return
|
||||
|
||||
else:
|
||||
# no appdir - means we should install "everything" for
|
||||
# the terminal, but we don't support that yet
|
||||
self.rprint(
|
||||
f"\n\t[bold red]sorry, full terminal install not yet supported[/bold red]\n"
|
||||
)
|
||||
self.rprint(
|
||||
f"\n\tPlease install the server, then terminal alongside that.\n"
|
||||
)
|
||||
sys.exit(2)
|
||||
|
||||
# do normal check
|
||||
super().check_appdir()
|
||||
|
||||
def get_install_type(self):
|
||||
|
||||
# prompt user
|
||||
|
|
@ -55,22 +104,91 @@ class InstallHandler(base.InstallHandler):
|
|||
# remember the answer
|
||||
self.install_type = install_type
|
||||
|
||||
if self.install_type != "server":
|
||||
self.rprint(
|
||||
"[bold red]sorry, terminal install is not yet implemented[/bold red]\n"
|
||||
)
|
||||
sys.exit(1)
|
||||
# and skip the continuum prompt
|
||||
self.wants_continuum = install_type == "server"
|
||||
|
||||
# install dependencies
|
||||
# now we can check the app dir; do this before further questions
|
||||
self.check_appdir()
|
||||
|
||||
# stop here for server, but we have more questions for terminal
|
||||
if self.install_type == "server":
|
||||
self.install_server_deps()
|
||||
return
|
||||
|
||||
def install_server_deps(self):
|
||||
subprocess.check_call(
|
||||
[sys.executable, "-m", "pip", "install", "WuttaPOS[server]"]
|
||||
self.rprint(
|
||||
"\n\n\t[blue]Next you must specify the Store and Terminal IDs.[/blue]"
|
||||
)
|
||||
self.rprint(
|
||||
"\n\tThese should match records in your DB; see the Server app for info.\n"
|
||||
)
|
||||
|
||||
def make_template_context(self, dbinfo, **kwargs):
|
||||
context = super().make_template_context(dbinfo, **kwargs)
|
||||
# store_id
|
||||
while not self.store_id:
|
||||
self.store_id = self.prompt_generic("Store ID")
|
||||
|
||||
# terminal_id
|
||||
while not self.terminal_id:
|
||||
self.terminal_id = self.prompt_generic("Terminal ID")
|
||||
|
||||
def install_app_deps(self):
|
||||
|
||||
if self.install_type == "server":
|
||||
subprocess.check_call(
|
||||
[sys.executable, "-m", "pip", "install", "WuttaPOS[server]"]
|
||||
)
|
||||
|
||||
elif self.install_type == "terminal":
|
||||
subprocess.check_call(
|
||||
[sys.executable, "-m", "pip", "install", "WuttaPOS[terminal]"]
|
||||
)
|
||||
|
||||
def get_db_url(self):
|
||||
if self.alongside:
|
||||
return self.config.appdb_engine.url
|
||||
|
||||
return super().get_db_url()
|
||||
|
||||
def make_template_context(self, **kwargs):
|
||||
context = super().make_template_context(**kwargs)
|
||||
|
||||
context["install_type"] = self.install_type
|
||||
context["store_id"] = self.store_id
|
||||
context["terminal_id"] = self.terminal_id
|
||||
|
||||
return context
|
||||
|
||||
def write_all_config_files(self, appdir, context):
|
||||
if self.alongside:
|
||||
# just want to add terminal.conf for this mode
|
||||
self.write_terminal_conf(appdir, context)
|
||||
return
|
||||
|
||||
# new app, so write normal files
|
||||
super().write_all_config_files(appdir, context)
|
||||
|
||||
def write_terminal_conf(self, appdir, context):
|
||||
term_context = dict(context)
|
||||
self.make_config_file(
|
||||
"terminal.conf.mako", os.path.join(appdir, "terminal.conf"), **term_context
|
||||
)
|
||||
|
||||
def install_db_schema(self, db_url, appdir=None):
|
||||
if self.alongside:
|
||||
# no need to install schema here
|
||||
return False
|
||||
|
||||
return super().install_db_schema(db_url, appdir=appdir)
|
||||
|
||||
def show_goodbye(self):
|
||||
"""
|
||||
Show the final message; this assumes setup completed okay.
|
||||
|
||||
This is normally called by :meth:`run()`.
|
||||
"""
|
||||
if self.alongside:
|
||||
self.rprint("\n\t[bold green]initial setup is complete![/bold green]")
|
||||
self.rprint("\n\tyou can run the terminal GUI app with:")
|
||||
self.rprint(f"\n\t[blue]cd {sys.prefix}[/blue]")
|
||||
self.rprint("\t[blue]bin/wuttapos -c app/terminal.conf run[/blue]\n")
|
||||
return
|
||||
|
||||
super().show_goodbye()
|
||||
|
|
|
|||
17
src/wuttapos/installer-templates/terminal.conf.mako
Normal file
17
src/wuttapos/installer-templates/terminal.conf.mako
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
## -*- mode: conf; -*-
|
||||
|
||||
<%text>############################################################</%text>
|
||||
#
|
||||
# ${app_title} - terminal installed alongside the server
|
||||
#
|
||||
<%text>############################################################</%text>
|
||||
|
||||
[wutta.config]
|
||||
require = %(here)s/wutta.conf
|
||||
|
||||
[wuttapos]
|
||||
store_id = ${store_id}
|
||||
terminal_id = ${terminal_id}
|
||||
|
||||
[wutta_continuum]
|
||||
enable_versioning = false
|
||||
|
|
@ -282,9 +282,11 @@ def main(page: ft.Page):
|
|||
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):
|
||||
# TODO: is there any way to inject a config object into the new Flet
|
||||
# app? if so this would be the place to do it. currently in main()
|
||||
# it always makes a new config for itself, as workaround. but also
|
||||
# see notes in the wuttapos.cli.run module
|
||||
def run_app():
|
||||
ft.app(target=main, assets_dir=resource_path("wuttapos.terminal:assets"))
|
||||
|
||||
|
||||
|
|
|
|||
46
tasks.py
Normal file
46
tasks.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaPOS -- Point of Sale system based on Wutta Framework
|
||||
# Copyright © 2026 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
|
||||
|
||||
|
||||
@task
|
||||
def release(c, skip_tests=False):
|
||||
"""
|
||||
Release a new version of WuttaPOS
|
||||
"""
|
||||
if not skip_tests:
|
||||
# c.run("pytest")
|
||||
pass
|
||||
|
||||
if os.path.exists("dist"):
|
||||
shutil.rmtree("dist")
|
||||
|
||||
c.run("python -m build --sdist")
|
||||
c.run("twine upload dist/*")
|
||||
Loading…
Add table
Add a link
Reference in a new issue