Compare commits

...

7 commits

Author SHA1 Message Date
c97b8c080e bump: version 0.4.1 → 0.4.2 2026-01-04 23:22:08 -06:00
bf6494f2a9 fix: tweak installer slightly, per upstream changes 2026-01-04 23:13:23 -06:00
4f86c4bcaa build: ignore pyc and hidden files
who knew, if you don't do that they wind up in the built sdist pkg
2026-01-03 21:15:33 -06:00
960dbfdee6 bump: version 0.4.0 → 0.4.1 2026-01-03 20:53:42 -06:00
7225d6b453 fix: add basic support for install/setup of terminal
only if installing alongside existing server app; can improve later
2026-01-03 20:52:44 -06:00
d6c1e688b1 docs: add link to online docs in readme 2026-01-03 17:47:59 -06:00
64f1e8897a infra: add release task 2026-01-03 17:44:50 -06:00
10 changed files with 279 additions and 30 deletions

3
.gitignore vendored
View file

@ -1 +1,4 @@
*~
*.pyc
dist/
docs/_build/

View file

@ -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.

View file

@ -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

View file

@ -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.

View file

@ -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",
]

View file

@ -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()

View file

@ -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()

View 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

View file

@ -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
View 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/*")